Каковы различия между «универсальными» типами в C ++ и Java? - PullRequest
145 голосов
/ 31 августа 2008

В Java есть дженерики, а C ++ обеспечивает очень сильную модель программирования с template s. Итак, в чем же разница между обобщениями C ++ и Java?

Ответы [ 12 ]

128 голосов
/ 31 августа 2008

Между ними большая разница. В 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.

РЕДАКТИРОВАТЬ: вышеупомянутое мнение о полезности было написано младшим "я". Обобщения Java помогают, конечно, с безопасностью типов.

117 голосов
/ 31 января 2009

Java Generics в массовом порядке отличается от шаблонов C ++.

В основном в C ++ шаблоны в основном представляют собой прославленный набор препроцессоров / макросов ( Примечание: , поскольку некоторые люди, похоже, не в состоянии понять аналогию, я не говорю, что обработка шаблонов - это макрос). В Java они в основном являются синтаксическим сахаром для минимизации шаблонного преобразования объектов. Вот довольно приличное введение в шаблоны C ++ против обобщений 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 появился настолько поздно, что они не хотели нарушать обратную совместимость (Map<String, String> можно использовать всякий раз, когда требуется Map). Сравните это с .Net / C #, где стирание типов не используется, что приводит ко всем видам различий (например, вы можете использовать примитивные типы, а IEnumerable и IEnumerable<T> не имеют никакого отношения друг к другу).

И класс, использующий дженерики, скомпилированные с помощью компилятора Java 5+, можно использовать в JDK 1.4 (при условии, что он не использует никаких других функций или классов, требующих Java 5+).

Вот почему Java Generics называют синтаксическим сахаром .

Но это решение о том, как сделать дженерики, имеет настолько глубокие последствия, что (10) FAQ по обобщению Java *1036* возникло, чтобы ответить на многие, многие вопросы о Generics Java.

Шаблоны C ++ имеют ряд функций, которых нет в Generics Java:

  • Использование аргументов примитивного типа.

    Например:

    template<class T, int i>
    class Matrix {
      int T[i][i];
      ...
    }
    

    Java не позволяет использовать аргументы примитивного типа в обобщениях.

  • Использование аргументов типа по умолчанию , который мне не хватает в Java, но для этого есть причины обратной совместимости;

  • Java позволяет ограничивать аргументы.

Например:

public class ObservableList<T extends List> {
  ...
}

Действительно нужно подчеркнуть, что вызовы шаблонов с разными аргументами действительно являются разными типами. Они даже не разделяют статических членов. В Java это не так.

Помимо различий с обобщениями, для полноты приведем базовое сравнение C ++ и Java еще одно ).

И я также могу предложить Мышление на Java . Как программист на C ++, многие понятия, такие как объекты, уже будут второй натурой, но есть небольшие различия, поэтому может быть целесообразно иметь вводный текст, даже если вы просматриваете части.

Многое из того, что вы узнаете при изучении Java, - это все библиотеки (обе стандартные - то, что входит в JDK - и нестандартные, которые включают в себя обычно используемые вещи, такие как Spring). Синтаксис Java более многословен, чем синтаксис C ++, и не имеет большого количества функций C ++ (например, перегрузка операторов, множественное наследование, механизм деструктора и т. Д.), Но это не делает его строго подмножеством C ++.

76 голосов
/ 31 августа 2008

C ++ имеет шаблоны. В Java есть дженерики, которые выглядят как шаблоны C ++, но они очень, очень разные.

Шаблоны работают, как следует из названия, предоставляя компилятору шаблон (подождите его ...), который он может использовать для генерации безопасного кода, заполняя параметры шаблона.

Обобщения, как я их понимаю, работают наоборот: параметры типа используются компилятором для проверки того, что код, использующий их, является типобезопасным, но полученный код генерируется без типов вообще.

Думайте о шаблонах C ++ как о действительно хорошей макросистеме, а обобщения Java как о инструменте для автоматической генерации типов.

15 голосов
/ 31 января 2009

Другая особенность шаблонов C ++, которой нет в дженериках Java, - это специализация. Это позволяет вам иметь другую реализацию для определенных типов. Таким образом, вы можете, например, иметь высоко оптимизированную версию для int , но при этом иметь универсальную версию для остальных типов. Или вы можете иметь разные версии для типов указателей и не указателей. Это очень удобно, если вы хотите работать с разыменованным объектом при наведении указателя.

13 голосов
/ 31 января 2009

Существует прекрасное объяснение этой темы в Обобщения и коллекции Java Морис Нафталин, Филипп Вадлер. Я очень рекомендую эту книгу. Цитировать:

Обобщения в Java напоминают шаблоны в C ++. ... Синтаксис намеренно похожи и семантика намеренно другой. ... Семантически, дженерики Java определяется стиранием, где как C ++ шаблоны определяются расширением.

Пожалуйста, прочитайте полное объяснение здесь .

alt text
(источник: oreilly.com )

5 голосов
/ 31 января 2009

В основном шаблоны AFAIK, C ++ создают копию кода для каждого типа, в то время как обобщенные типы Java используют точно такой же код.

Да, вы можете сказать , что шаблон C ++ эквивалентен универсальной концепции Java (хотя правильнее было бы сказать, что универсальные Java-символы эквивалентны C ++ по концепции)

Если вы знакомы с механизмом шаблонов C ++, вы можете подумать, что дженерики похожи, но сходство поверхностно. Дженерики не генерируют новый класс для каждой специализации и не допускают «шаблонного метапрограммирования».

от: Обобщения Java

3 голосов
/ 03 сентября 2008

Еще одним преимуществом шаблонов 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 с помощью указателей, будет вызван второй метод, если вы вызовете sum с не указательными объектами, будет вызван первый метод, а если вы вызовете sum с Special объектами, третий будет называться. Я не думаю, что это возможно с Java.

3 голосов
/ 03 сентября 2008

Обобщения Java (и C #) кажутся простым механизмом подстановки типов во время выполнения.
Шаблоны C ++ - это конструкция времени компиляции, которая дает вам возможность изменить язык в соответствии с вашими потребностями На самом деле это чисто функциональный язык, который компилятор выполняет во время компиляции.

2 голосов
/ 31 января 2016

Я подведу итог в одном предложении: шаблоны создают новые типы, универсальные ограничения ограничивают существующие типы.

1 голос
/ 01 июля 2018

Ответ, приведенный ниже, взят из книги Взлом интервью по кодированию Решения для главы 13, что я считаю очень хорошим.

Реализация обобщений Java основана на идее «стирания типов»: этот метод исключает параметризованные типы, когда исходный код транслируется в байт-код виртуальной машины Java (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, использующие Tow, будут использовать статическую переменную.

/*** 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 и шаблонов C ++ есть ряд других отличий. К ним относятся:

  • Шаблоны C ++ могут использовать примитивные типы, такие как int. Ява не может и должна вместо этого используйте Integer.
  • В Java вы можете ограничить параметры типа шаблона определенный тип. Например, вы можете использовать дженерики для реализации CardDeck и укажите, что параметр типа должен расширяться от CardGame.
  • В C ++ параметр типа может быть создан, в то время как в Java нет поддержите это.
  • В Java параметр типа (то есть Foo в MyClass) не может быть используется для статических методов и переменных, поскольку они будут разделены между MyClass и MyClass. В C ++ эти классы различны, поэтому параметр типа может использоваться для статических методов и переменных.
  • В Java все экземпляры MyClass, независимо от параметров их типа, имеют одинаковый тип. Параметры типа стираются во время выполнения. В C ++ экземпляры с разными параметрами типов являются разными типами.
...