Почему Java Generics не поддерживает примитивные типы?

Почему дженерики в Java работают с классами, но не с примитивными типами?

Например, это нормально работает:

List<Integer> foo = new ArrayList<Integer>();

но это не разрешено:

List<int> bar = new ArrayList<int>();

person Saurabh Gokhale    schedule 27.04.2010    source источник
comment
int i = (int) новый объект (); компилируется просто отлично.   -  person Sachin Verma    schedule 26.02.2018


Ответы (5)


Обобщения в Java полностью создаются во время компиляции - компилятор превращает все общие применения в приведение к правильному типу. Это сделано для обеспечения обратной совместимости с предыдущими средами выполнения JVM.

Этот:

List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);

превращается в (примерно):

List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);

Итак, все, что используется в качестве универсальных, должно быть преобразовано в Object (в этом примере get(0) возвращает Object), а примитивные типы - нет. Поэтому их нельзя использовать в дженериках.

person thecoop    schedule 27.04.2010
comment
Так что, дженерики в Java немного похожи на шаблоны C ++? Можете ли вы получить раздувание кода? - person Danyal Aytekin; 27.04.2010
comment
@danyal: Нет - одна и та же реализация класса используется для всех общих целей. Все это реализовано с использованием прямого преобразования в нужные типы в нужные места и из них. - person thecoop; 27.04.2010
comment
Последнее предложение неверно; C # всегда поддерживал ко / контравариантность для отдельных типов, точно так же, как Java. См. where ключевое слово - person BlueRaja - Danny Pflughoeft; 14.10.2011
comment
@DanyalAytekin - На самом деле, дженерики Java вообще НЕ обрабатываются как шаблоны C ++ ... - person Stephen C; 26.03.2012
comment
Почему компилятор Java не может также упаковать примитивный тип до того, как он будет использован? Это должно быть возможно, правда? - person vrwim; 03.01.2014
comment
@vrwim - Возможно. Но это был бы просто синтаксический сахар. Настоящая проблема заключается в том, что универсальные Java-шаблоны с примитивами в штучной упаковке относительно дороги как во времени, так и в пространстве по сравнению с моделью C ++ / C # ... где используется фактический тип примитивов. - person Stephen C; 23.06.2015
comment
@Ced @vrvim никто не запрещает вам использовать типы обертки, например, List<Integer>. Java будет упаковывать / распаковывать по мере необходимости, поэтому нет проблем с вводом и выводом чисел. - person MauganRa; 23.02.2017
comment
@MauganRa что это? Потоки? IntStream, LongStream, DoubleStream. Проблема в том, что java запрещает делать List ‹int›, Stream ‹int› - person Ced; 23.02.2017
comment
@ Сид, я знаю. Я хочу сказать, что вы всегда можете использовать типы оболочки, хотя это, вероятно, приведет к проблемам в другом месте, поскольку принятый ответ демонстрирует stackoverflow.com/questions/5199359/. - person MauganRa; 24.02.2017
comment
@MauganRa да, я знаю, что смогу :) Я твердо уверен, что это ужасный дизайн. Надеюсь, это будет исправлено в java 10 (по крайней мере, я слышал), а также в функциях более высокого порядка. Не цитируйте меня по этому поводу. - person Ced; 24.02.2017
comment
@Ced полностью согласен с тем, что это плохой дизайн, который бесконечно вредит как новичкам, так и профессионалам. - person MauganRa; 26.02.2017
comment
Разве компилятор не может создавать шаблоны, как компиляторы C, создавая примитивный метод как перегрузку? Или какие-то новые языки на основе JVM уже делают это? - person SOFe; 20.01.2018
comment
Ответ людям, которые настаивают на том, что это плохой дизайн, состоит в том, что это должно быть так. Альтернативой были серьезные изменения в языке Java, которые потребовали бы от существующих (платящих) клиентов Sun / Oracle просматривать миллиарды строк исходного кода. Это было неприемлемо. (Это было в 2004 году. Объем проверки кода в 2018 году будет на 2 или 3 порядка больше.) - person Stephen C; 15.10.2019
comment
И нет, это не будет исправлено в какой-нибудь будущей версии Java, потому что это все еще будет серьезным критическим изменением. Это было бы похоже на переворот Python 2 - ›3. Корпоративные (платящие) клиенты этого не потерпят. (Прочтите - ИТ-директор вашей компании ... парень, которому нужно найти человеческие ресурсы для выполнения работы и который решает, какие языки программирования будут использоваться в будущем.) - person Stephen C; 15.10.2019
comment
@StephenC: Я удивлен вашим заявлением о том, что поддержка List<int> потребует критических изменений. Если было желание реализовать, то почему нельзя было реализовать с помощью автобокса / автооткрытия и стирания? - person ruakh; 11.03.2020
comment
@ruakh - Звучит в точности как List<Integer> плюс немного синтаксического сахара. На самом деле это ничего не решает. Производительность будет такой же, как и раньше. Накладные расходы на производительность упаковки / распаковки, до 24 байтов на int элемент (в ArrayList) по сравнению с 4 байтами для int[], дополнительные накладные расходы GC и т. Д. - person Stephen C; 12.03.2020
comment
@StephenC: Верно. Я думал, вы говорили, что это было невозможно сделать обратно-совместимым способом, но я полагаю, вы на самом деле просто говорили, что это не будет иметь желаемых преимуществ, если будет сделано обратно-совместимым способом. . OK. - person ruakh; 12.03.2020
comment
Да. Решение без желаемых преимуществ решения не является решением. В этом случае решение возможно, но не решение. Но это просто повторение того, что я уже сказал; см. мой предыдущий комментарий к vrwim выше. Это контекст для моих последующих комментариев. - person Stephen C; 12.03.2020
comment
@StephenC Никогда не говори никогда. Но завтра не придет ... - person Holger; 26.05.2021

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

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


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

person Stephen C    schedule 27.04.2010
comment
Спасибо за этот ответ. Поскольку я не знаю, как работали более старые версии Java, не могли бы вы привести пример того, как это могло бы нарушить какой-то старый код, если бы примитивы поддерживались дженериками? - person shiva; 23.05.2021

В java универсальные шаблоны реализованы с помощью «стирания типа» для обратной совместимости. Все универсальные типы преобразуются в Object во время выполнения. Например,

public class Container<T> {

    private T data;

    public T getData() {
        return data;
    }
}

будет отображаться во время выполнения как,

public class Container {

    private Object data;

    public Object getData() {
        return data;
    }
}

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

Container<Integer> val = new Container<Integer>();
Integer data = val.getData()

станет

Container val = new Container();
Integer data = (Integer) val.getData()

Теперь вопрос в том, почему «Объект» выбран в качестве типа во время выполнения?

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

Поскольку все примитивы не наследуются от «Object», мы не можем использовать его в качестве универсального типа.

К вашему сведению: Project Valhalla пытается решить указанную выше проблему.

person Piyush Sagar    schedule 06.04.2018
comment
Плюс 1 для правильной номенклатуры. - person Drazen Bjelovuk; 25.02.2020

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

Предполагается, что это будет в Java 10 в рамках Project Valhalla.

В статье Брайана Гетца на Статус специализации

Есть отличное объяснение О причине, по которой generic не поддерживаются для примитивов. И как это будет реализовано в будущие выпуски Java.

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

...

была выбрана стратегия однородной трансляции, при которой переменные универсального типа стираются до их границ по мере включения в байт-код. Это означает, что независимо от того, является ли класс универсальным или нет, он по-прежнему компилируется в один класс с тем же именем и с одинаковыми сигнатурами членов. Безопасность типов проверяется во время компиляции, и время выполнения не ограничивается системой универсальных типов. В свою очередь, это накладывало ограничение на то, что универсальные шаблоны могут работать только с ссылочными типами, поскольку Object является наиболее общим доступным типом и не распространяется на примитивные типы.

person vinS    schedule 18.12.2017

Для коллекций требуется тип, производный от java.lang.Object. Базовые типы этого просто не делают.

person ZeissS    schedule 27.04.2010
comment
Думаю, вопрос в том, почему. Почему для дженериков требуются объекты? Похоже, что консенсус заключается в том, что это не столько выбор дизайна, сколько отказ от обратной совместимости. На мой взгляд, если дженерики не могут обрабатывать примитивы, это недостаток функциональности. В его нынешнем виде все, что связано с примитивами, должно быть написано для каждого примитива: вместо Comparator ‹t, t› у нас есть Integer.compare (int a, int b), Byte.compare (byte a, byte b) и т. Д. Это не выход! - person John P; 27.06.2014
comment
Да, дженерики по сравнению с примитивными типами - это обязательная функция. Вот ссылка на его предложение openjdk.java.net/jeps/218 - person crow; 24.12.2017