Чем дженерики Java отличаются от шаблонов C ++? Почему я не могу использовать int в качестве параметра? - PullRequest
49 голосов
/ 15 июня 2009

Я пытаюсь создать

ArrayList<int> myList = new ArrayList<int>();

в Java, но это не работает.

Может кто-нибудь объяснить, почему int как параметр типа не работает?
Использование Integer класса для int примитивных работ, но может кто-нибудь объяснить, почему int не принят?

Java версия 1.6

Ответы [ 7 ]

64 голосов
/ 15 июня 2009

Обобщения Java настолько отличаются от шаблонов C ++, что я не собираюсь перечислять здесь различия. (См. В чем различия между «универсальными» типами в C ++ и Java? для получения более подробной информации.)

В данном конкретном случае проблема заключается в том, что вы не можете использовать примитивы в качестве параметров универсального типа (см. JLS § 4.5.5 : «Аргументы типа могут быть ссылочными типами или подстановочными знаками».) *

Однако из-за автобокса вы можете делать такие вещи, как:

List<Integer> ints = new ArrayList<Integer>();
ints.add(3); // 3 is autoboxed into Integer.valueOf(3)

Так что это снимает часть боли. Тем не менее, это определенно сказывается на эффективности времени выполнения.

25 голосов
/ 15 июня 2009

Причина, по которой int не работает, заключается в том, что вы не можете использовать примитивные типы в качестве общих параметров в Java.

Что касается вашего реального вопроса, как шаблоны C ++ отличаются от шаблонов Java, ответ ... действительно очень отличается. По сути, это два совершенно разных подхода к реализации аналогичного конечного эффекта.

Java имеет тенденцию сосредотачиваться на определении универсального. То есть валидность общего определения проверяется только с учетом кода в общем. Если параметры не ограничены должным образом, определенные действия не могут быть выполнены с ними. Фактический тип, с которым он в конечном счете вызывается, не рассматривается.

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

9 голосов
/ 15 июня 2009

Это очень разные понятия, которые можно использовать для выполнения некоторых, но не всех одинаковых задач. Как сказано в других ответах, для того, чтобы преодолеть все различия, потребуется немало времени, но вот то, что я вижу в качестве широких штрихов.

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

Шаблоны позволяют создавать полиморфные контейнеры времени компиляции посредством нескольких экземпляров (а также метапрограммирования шаблонов путем предоставления (в настоящее время слабо типизированного) языка в системе типов c ++.). Это учитывает специализации для данных типов, недостатком которых является потенциальное «раздувание кода» из-за необходимости более чем одной скомпилированной реализации.

Шаблоны более мощные, чем дженерики; первый фактически является другим языком, встроенным в c ++, хотя, насколько мне известно, последний полезен только в контейнерах

6 голосов
/ 03 марта 2017

Основное отличие состоит в том, как они реализованы, но их имена точно описывают их реализацию.

Шаблоны ведут себя как шаблоны. Итак, если вы напишите:

template<typename T>
void f(T s)
{
    std::cout << s << '\n';
}

...
int x = 0;
f(x);
...

Компилятор применяет шаблон, поэтому в конце компилятор обрабатывает код следующим образом:

void f_generated_with_int(int s)
{
    std::cout << s << '\n';
}

...
int x = 0;
f_generated_with_int(x);
...

Таким образом, для каждого типа, который используется для вызова f, генерируется новый код.

С другой стороны, дженерики проверяются только по типам, но затем вся информация о типах стирается. Итак, если вы напишите:

class X<T> {
    private T x;

    public T getX() { return x; }
    public void setX(T x) { this.x = x; }
}

...
Foo foo = new Foo();
X<Foo> x = new X<>();
x.setX(foo);
foo = x.getX();
...

Java компилирует это так:

class X {
    private Object x;

    public Object getX() { return x; }
    public void setX(Object x) { this.x = x; }
}

...
Foo foo = new Foo();
X x = new X();
x.setX(foo);
foo = (Foo)x.getX();
...

В конце концов:

  • требуют создания экземпляров каждого вызова шаблонной функции (при компиляции каждого файла .cpp), поэтому шаблоны компилируются медленнее
  • с дженериками вы не можете использовать примитивы, потому что они не Object, поэтому дженерики менее универсальны
3 голосов
/ 15 июня 2009

это потому, что int является примитивом, это известная проблема .

Если вы действительно хотите, вы можете создать подкласс / написать свою собственную коллекцию, которая может это сделать.

3 голосов
/ 15 июня 2009

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

1 голос
/ 17 июня 2009

Вы можете попробовать TIntArraList из GNU Trove, который будет действовать как ArrayList значений int.

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