Почему дженерики в Java работают с классами, но не с примитивными типами?
Например, это нормально работает:
List<Integer> foo = new ArrayList<Integer>();
но это не разрешено:
List<int> bar = new ArrayList<int>();
Почему дженерики в Java работают с классами, но не с примитивными типами?
Например, это нормально работает:
List<Integer> foo = new ArrayList<Integer>();
но это не разрешено:
List<int> bar = new ArrayList<int>();
Обобщения в 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
), а примитивные типы - нет. Поэтому их нельзя использовать в дженериках.
List<Integer>
. Java будет упаковывать / распаковывать по мере необходимости, поэтому нет проблем с вводом и выводом чисел.
- person MauganRa; 23.02.2017
List<int>
потребует критических изменений. Если было желание реализовать, то почему нельзя было реализовать с помощью автобокса / автооткрытия и стирания?
- person ruakh; 11.03.2020
List<Integer>
плюс немного синтаксического сахара. На самом деле это ничего не решает. Производительность будет такой же, как и раньше. Накладные расходы на производительность упаковки / распаковки, до 24 байтов на int
элемент (в ArrayList
) по сравнению с 4 байтами для int[]
, дополнительные накладные расходы GC и т. Д.
- person Stephen C; 12.03.2020
В Java универсальные шаблоны работают так же, как и они ... по крайней мере частично ... потому что они были добавлены в язык через несколько лет после его разработки 1. Разработчики языка были ограничены в своих вариантах универсальных шаблонов, поскольку должны были разработать дизайн, который был обратно совместим с существующим языком и библиотекой классов Java.
Другие языки программирования (например, C ++, C #, Ada) позволяют использовать примитивные типы в качестве типов параметров для универсальных типов. Но оборотной стороной этого является то, что реализации универсальных типов (или типов шаблонов) в таких языках обычно влекут за собой создание отдельной копии универсального типа для каждой параметризации типа.
1 - дженерики не были включены в Java 1.0 из-за нехватки времени. Они чувствовали, что им нужно быстро выпустить язык Java, чтобы заполнить новые рыночные возможности, представленные веб-браузерами. Джеймс Гослинг заявил, что он хотел бы включить дженерики, если бы у них было время. Как выглядел бы язык Java, если бы это произошло, остается только догадываться.
В 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 пытается решить указанную выше проблему.
Согласно документации Java переменные общего типа могут быть создаются ссылочными типами, а не примитивными типами.
Предполагается, что это будет в Java 10 в рамках Project Valhalla.
В статье Брайана Гетца на Статус специализации
Есть отличное объяснение О причине, по которой generic не поддерживаются для примитивов. И как это будет реализовано в будущие выпуски Java.
Текущая стертая реализация Java, которая создает один класс для всех эталонных экземпляров и не поддерживает примитивные экземпляры. (Это однородный перевод, и ограничение на то, что универсальные шаблоны Java могут ограничиваться только ссылочными типами, происходит из-за ограничений однородного преобразования в отношении набора байт-кода JVM, который использует разные байт-коды для операций с ссылочными типами по сравнению с примитивными типами.) Однако стертые универсальные шаблоны в Java обеспечивают как поведенческую параметричность (универсальные методы), так и параметричность данных (необработанные и подстановочные экземпляры универсальных типов).
...
была выбрана стратегия однородной трансляции, при которой переменные универсального типа стираются до их границ по мере включения в байт-код. Это означает, что независимо от того, является ли класс универсальным или нет, он по-прежнему компилируется в один класс с тем же именем и с одинаковыми сигнатурами членов. Безопасность типов проверяется во время компиляции, и время выполнения не ограничивается системой универсальных типов. В свою очередь, это накладывало ограничение на то, что универсальные шаблоны могут работать только с ссылочными типами, поскольку Object является наиболее общим доступным типом и не распространяется на примитивные типы.
Для коллекций требуется тип, производный от java.lang.Object
. Базовые типы этого просто не делают.