В чем различия между “универсальными” типами в C ++ и Java?

StackOverflow https://stackoverflow.com/questions/36347

Вопрос

Java имеет универсальные методы, а C ++ предоставляет очень сильную модель программирования с templates.Итак, в чем же разница между C ++ и Java generics?

Это было полезно?

Решение

Между ними есть большая разница. В 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 используется метод под названием «erasure». где универсальный тип стирается во время выполнения, поэтому во время выполнения Java фактически вызывает ...

Something sum(Something a, Something b) { return a.add ( b ); }

Так что общее программирование на Java не очень полезно, это всего лишь небольшой синтаксический сахар, чтобы помочь с новой конструкцией foreach.

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

Другие советы

Дженерики Java - это массово отличается от шаблонов C ++.

По сути, в C ++ шаблоны - это, по сути, прославленный набор препроцессоров / макросов (Примечание: поскольку некоторые люди, похоже, не в состоянии понять аналогию, я не говорю, что обработка шаблонов - это макрос).В Java они в основном представляют собой синтаксический сахар, позволяющий минимизировать шаблонное приведение объектов.Вот довольно приличный введение в шаблоны C ++ и Java generics.

Чтобы подробнее остановиться на этом моменте:когда вы используете шаблон C ++, вы, по сути, создаете другую копию кода, точно так же, как если бы вы использовали #define макрос.Это позволяет вам делать такие вещи, как иметь int параметры в определениях шаблонов, которые определяют размеры массивов и тому подобное.

Java так не работает.В Java все объекты имеют протяженность от java.lang.Объект итак, до создания дженериков вы бы написали такой код:

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

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

Вот почему Java-дженерики называются синтаксический сахар.

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

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

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

    Например:

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

    Java не допускает использования аргументов примитивного типа в generics.

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

  • Java допускает ограничение аргументов.

Например:

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

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

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

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

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

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

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

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

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

 

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

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

  

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

Прочитайте подробное объяснение # PPA6, M1 "rel =" nofollow noreferrer "> здесь .

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

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

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

  

Если вы знакомы с механизмом шаблонов C ++, вы можете подумать, что дженерики похожи, но сходство поверхностно. Обобщения не генерируют новый класс для каждой специализации и не разрешают & # 8220; шаблонное метапрограммирование. & # 8221;

от: Generics Java

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

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

@Кит:

Этот код на самом деле неправильный, не считая небольших сбоев (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); }

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

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

Это сильно отличается от C ++.В C ++ шаблоны - это, по сути, набор прославленных макросов, при этом компилятор создает новую копию кода шаблона для каждого типа.Доказательством этого является тот факт, что экземпляр MyClass не будет совместно использовать статическую переменную с MyClass.Однако экземпляры Tow 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 и шаблоны C ++ имеют ряд других отличий.К ним относятся:

  • Шаблоны C ++ могут использовать примитивные типы, такие как int.Java не может и должна вместо этого использовать Integer .
  • В Java вы можете ограничить параметры типа шаблона, чтобы они относились к определенному типу.Например, вы могли бы использовать generics для реализации CardDeck и указать, что параметр type должен расширяться от CardGame .
  • В C ++ параметр type может быть создан в виде экземпляра, тогда как Java этого не поддерживает.
  • В Java параметр type (т. е. Foo в MyClass) не может быть использован для статических методов и переменных, поскольку они будут разделяться между MyClass и MyClass-ом.В C++ эти классы разные, поэтому параметр type можно использовать для статических методов и переменных.
  • В Java все экземпляры MyClass, независимо от их параметров типа, имеют один и тот же тип.Параметры типа удаляются во время выполнения.В 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 ++, и он не будет компилироваться, жалуясь на бесконечное расширение шаблонов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top