Відповіді:
Між ними велика різниця. У C ++ вам не потрібно вказувати клас або інтерфейс для загального типу. Ось чому ви можете створити по-справжньому загальні функції та класи, зауважуючи про певніше введення.
template <typename T> T sum(T a, T b) { return a + b; }
Наведений вище метод додає два об'єкти одного типу і може використовуватися для будь-якого типу T, у якого доступний оператор "+".
У Java вам потрібно вказати тип, якщо ви хочете викликати методи переданих об'єктів, наприклад:
<T extends Something> T sum(T a, T b) { return a.add ( b ); }
У C ++ загальні функції / класи можуть бути визначені лише в заголовках, оскільки компілятор генерує різні функції для різних типів (на які він викликається). Тож компіляція проходить повільніше. У Java для компіляції немає головного штрафу, але Java використовує техніку під назвою "стирання", коли загальний тип стирається під час виконання, тому під час виконання Java насправді викликає ...
Something sum(Something a, Something b) { return a.add ( b ); }
Тож загальне програмування на Java не дуже корисне, це лише трохи синтаксичного цукру, щоб допомогти з новою конструкцією foreach.
EDIT: думку вище про корисність написала молодша власна особа. Загальна інформація Java допомагає забезпечити безпеку типу.
extends
або super
. Відповідь невірна,
Java Generics масово відрізняється від шаблонів C ++.
В основному шаблони C ++ - це в основному прославлений препроцесор / набір макросів ( Примітка: оскільки деякі люди здаються нездатними зрозуміти аналогію, я не кажу, що обробка шаблону є макросом). На Яві вони в основному синтаксичні цукру, щоб мінімізувати викидання об'єктів на колонці. Ось досить пристойний вступ до шаблонів C ++ та generic Java .
Щоб детальніше зупинитися на цьому: коли ви використовуєте шаблон C ++, ви в основному створюєте іншу копію коду так само, як якщо б ви використовували #define
макрос. Це дозволяє робити такі речі, як у вас єint
параметри у визначеннях шаблонів, які визначають розміри масивів тощо.
Java не працює так. У Java всі об'єкти віддалені від java.lang.Object, таким чином, попередньо Generics, ви б написали такий код:
public class PhoneNumbers {
private Map phoneNumbers = new HashMap();
public String getPhoneNumber(String name) {
return (String)phoneNumbers.get(name);
}
...
}
тому що всі типи колекцій Java використовували Object як базовий тип, щоб ви могли помістити в них що завгодно. Java 5 обертається навколо і додає дженерики, щоб ви могли робити такі речі:
public class PhoneNumbers {
private Map<String, String> phoneNumbers = new HashMap<String, String>();
public String getPhoneNumber(String name) {
return phoneNumbers.get(name);
}
...
}
І ось все Java Generics - це: обгортки для кастингу об'єктів. Це тому, що Java Generics не вдосконалюються. Вони використовують стирання типу. Це рішення було прийняте тому, що Java Generics з'явилася настільки пізно, що вони не хотіли порушувати відсталу сумісність (a Map<String, String>
може бути використана всякий раз, коли Map
викликається необхідність). Порівняйте це з .Net / C #, де стирання типу не використовується, що призводить до різного роду відмінностей (наприклад, ви можете використовувати примітивні типи та IEnumerable
іIEnumerable<T>
не маєте ніякого відношення один до одного).
А клас, що використовує дженерики, зібрані з компілятором Java 5+, можна використовувати на JDK 1.4 (якщо припускати, що він не використовує жодних інших функцій чи класів, для яких потрібна Java 5+).
Ось чому Java Generics називають синтаксичним цукром .
Але це рішення про те, як робити генеріки, має настільки глибокі ефекти, що (чудовий) FAQ щодо генеріалів Java з’явився, щоб відповісти на безліч питань, що виникають у людей щодо Java Generics.
Шаблони C ++ мають ряд функцій, яких немає у Java Generics:
Використання аргументів примітивного типу.
Наприклад:
template<class T, int i>
class Matrix {
int T[i][i];
...
}
Java не дозволяє використовувати аргументи примітивного типу в генеріках.
Використання аргументів типу за замовчуванням , що є однією з особливостей, які мені не вистачає в Java, але для цього є причини зворотної сумісності;
Наприклад:
public class ObservableList<T extends List> {
...
}
Дійсно потрібно підкреслити, що виклики шаблонів з різними аргументами насправді є різними типами. Вони навіть не діляться статичними членами. У Java це не так.
Окрім відмінностей у генериці, для повноти, ось базове порівняння C ++ та Java (та ще одна ).
І я також можу запропонувати Мислення на Java . Як програміст на C ++, багато таких понять, як об'єкти, будуть вже другою природою, але існують тонкі відмінності, тому може бути вартим вступний текст, навіть якщо ви знімаєте частини.
Багато чого, що ви дізнаєтесь, вивчаючи Java, - це всі бібліотеки (обидві стандартні - що входить у JDK - і нестандартні, що включають в себе загальновживані речі, такі як Spring). Синтаксис Java є більш багатослівним, ніж синтаксис C ++ і не має великої кількості функцій C ++ (наприклад, перевантаження оператора, багаторазове успадковування, механізм деструктора тощо), але це не суворо робить його також підмножиною C ++.
Map map = new HashMap<String, String>
. Це означає, що ви можете розгорнути новий код на старому JVM, і він запуститься через подібність у байт-коді.
C ++ має шаблони. У Java є дженерики, які виглядають начебто на зразок шаблонів C ++, але вони дуже-дуже різні.
Шаблони працюють, як випливає з назви, надаючи компілятору шаблон (зачекайте його ...), який він може використовувати для створення безпечного типу коду, заповнюючи параметри шаблону.
Як я їх розумію, дженерики працюють навпаки: параметри типу використовуються компілятором для перевірки того, що код, що використовує їх, безпечний для типу, але отриманий код генерується без типів взагалі.
Подумайте про шаблони C ++ як справді хорошу макросистему, а дженерику Java як інструмент для автоматичного генерування набору типів.
const
. Об'єкт в C ++ не буде змінено через const
покажчик, якщо тільки const
-ness не буде відкинуто. Аналогічно, неявні касти, створені загальними типами на Java, гарантовано є "безпечними", якщо параметри типу не відкидаються вручну десь у коді.
Ще одна особливість, що у шаблонах C ++ є те, що дженерики Java не є спеціалізацією. Це дозволяє мати різну реалізацію для конкретних типів. Таким чином, ви можете, наприклад, мати оптимізовану версію для int , тоді як для решти типів ще маєте загальну версію. Або ви можете мати різні версії для типів вказівника та не вказівника. Це стане в нагоді, якщо ви хочете оперувати об'єктом, що перебуває під час передачі вказівника.
Цю тему є чудовим поясненням у Java Generics and Collections Моріса Нафталіна, Філіпа Вадлера. Я дуже рекомендую цю книгу. Цитувати:
Дженріки на Java нагадують шаблони на C ++. ... Синтаксис свідомо схожий, а семантика навмисно різна. ... Семантично, дженерики Java визначаються стиранням, де як C ++ шаблони визначаються розширенням.
Прочитайте повне пояснення тут .
(джерело: oreilly.com )
В основному, шаблони AFAIK, C ++ створюють копію коду для кожного типу, тоді як Java generics використовує абсолютно той самий код.
Так, ви можете сказати, що шаблон C ++ еквівалентний загальній концепції Java (хоча правильніше було б сказати, що дженерики Java в концепції еквівалентні C ++)
Якщо ви знайомі з механізмом шаблонів C ++, ви можете подумати, що дженерики схожі, але подібність поверхнева. Generics не створює новий клас для кожної спеціалізації, а також не дозволяє "метапрограмування шаблонів".
від: Java Generics
Загальна інформація про Java (і C #) представляє собою простий механізм заміни типу виконання.
Шаблони C ++ - це конструкція часу компіляції, яка дає вам змогу змінити мову відповідно до ваших потреб. Вони фактично є чисто функціональною мовою, яку компілятор виконує під час компіляції.
Ще одна перевага шаблонів C ++ - спеціалізація.
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Special sum(const Special& a, const Special& b) { return a.plus(b); }
Тепер, якщо ви будете називати суму за допомогою покажчиків, викличе другий метод, якщо ви будете викликати суму з не вказівними об'єктами, викличеться перший метод, а якщо ви зателефонуєте sum
з Special
об'єктами, буде викликаний третій. Я не думаю, що це можливо для Java.
Я підсумую його в одному реченні: шаблони створюють нові типи, генерика обмежує існуючі типи.
@Keith:
Цей код насправді неправильний, окрім менших глюків ( template
опущений, синтаксис спеціалізації виглядає інакше), часткова спеціалізація не працює на шаблонах функцій, лише на шаблонах класів. Код, однак, працює без часткової спеціалізації шаблону, замість цього використовуючи звичайну стару перевантаження:
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Відповідь нижче - із книги Cracking The Coding Interview Solutions до розділу 13, що, на мою думку, дуже добре.
Реалізація дженерики Java базується на ідеї "стирання типу". Ця методика виключає параметризовані типи, коли вихідний код переводиться на байт-код Java Virtual Machine (JVM). Наприклад, припустимо, що у вас є код Java нижче:
Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);
Під час компіляції цей код переписується на:
Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);
Використання Java-дженериків насправді мало змінило наші можливості; це просто зробило речі трохи гарнішими. З цієї причини, дженерики Java іноді називають "синтаксичним цукром:".
Це сильно відрізняється від C ++. У C ++ шаблони, по суті, є прославленим набором макросів, компілятор створює нову копію коду шаблону для кожного типу. Доказом цього є той факт, що екземпляр MyClass не поділяє статичну змінну зMyClass. Однак, екземпляри MyClass матимуть статичну змінну.
/*** MyClass.h ***/
template<class T> class MyClass {
public:
static int val;
MyClass(int v) { val v;}
};
/*** MyClass.cpp ***/
template<typename T>
int MyClass<T>::bar;
template class MyClass<Foo>;
template class MyClass<Bar>;
/*** main.cpp ***/
MyClass<Foo> * fool
MyClass<Foo> * foo2
MyClass<Bar> * barl
MyClass<Bar> * bar2
new MyClass<Foo>(10);
new MyClass<Foo>(15);
new MyClass<Bar>(20);
new MyClass<Bar>(35);
int fl fool->val; // will equal 15
int f2 foo2->val; // will equal 15
int bl barl->val; // will equal 35
int b2 bar2->val; // will equal 35
У Java статичні змінні поділяються між екземплярами MyClass, незалежно від параметрів різного типу.
Java generics та шаблони C ++ мають ряд інших відмінностей. До них належать:
Шаблони - це не що інше, як макросистема. Синтаксичний цукор. Вони повністю розширюються до фактичної компіляції (або, принаймні, компілятори ведуть себе так, як ніби це було).
Приклад:
Скажімо, ми хочемо дві функції. Одна функція займає дві послідовності (список, масиви, вектори, що стосується) чисел і повертає їх внутрішній добуток. Інша функція бере довжину, генерує дві послідовності такої довжини, передає їх першій функції та повертає результат. Суть полягає в тому, що ми можемо помилитися у другій функції, так що ці дві функції насправді не мають однакової довжини. Нам потрібен компілятор, щоб попередити нас у цьому випадку. Не коли програма працює, а коли вона компілюється.
На Java ви можете зробити щось подібне:
import java.io.*;
interface ScalarProduct<A> {
public Integer scalarProduct(A second);
}
class Nil implements ScalarProduct<Nil>{
Nil(){}
public Integer scalarProduct(Nil second) {
return 0;
}
}
class Cons<A implements ScalarProduct<A>> implements ScalarProduct<Cons<A>>{
public Integer value;
public A tail;
Cons(Integer _value, A _tail) {
value = _value;
tail = _tail;
}
public Integer scalarProduct(Cons<A> second){
return value * second.value + tail.scalarProduct(second.tail);
}
}
class _Test{
public static Integer main(Integer n){
return _main(n, 0, new Nil(), new Nil());
}
public static <A implements ScalarProduct<A>>
Integer _main(Integer n, Integer i, A first, A second){
if (n == 0) {
return first.scalarProduct(second);
} else {
return _main(n-1, i+1,
new Cons<A>(2*i+1,first), new Cons<A>(i*i, second));
//the following line won't compile, it produces an error:
//return _main(n-1, i+1, first, new Cons<A>(i*i, second));
}
}
}
public class Test{
public static void main(String [] args){
System.out.print("Enter a number: ");
try {
BufferedReader is =
new BufferedReader(new InputStreamReader(System.in));
String line = is.readLine();
Integer val = Integer.parseInt(line);
System.out.println(_Test.main(val));
} catch (NumberFormatException ex) {
System.err.println("Not a valid number");
} catch (IOException e) {
System.err.println("Unexpected IO ERROR");
}
}
}
В C # можна написати майже те саме. Спробуйте переписати його на C ++, і він не збиратиметься, скаржившись на нескінченне розширення шаблонів.
Я хотів би навести тут питання про різницю :
Основна відмінність C ++ від Java полягає в їх залежності від платформи. Хоча, C ++ - це залежна від платформи мова, Java - незалежна від платформи мова.
Наведене вище твердження є причиною того, що C ++ може надати справжні загальні типи. У той час як у Java є чітка перевірка, і тому вони не дозволяють використовувати дженерики так, як це дозволяє C ++.