Почему дженерики в 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>();
Generics в 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);
Итак, все, что используется как generics, должно быть преобразовано в Object (в этом примере get(0)
возвращает Object
), а примитивные типы - нет. Поэтому они не могут использоваться в дженериках.
В Java дженерики работают так, как они делают... хотя бы частично... потому что они были добавлены к языку через несколько лет после того, как язык был разработан 1. Разработчики языка были ограничены в своих вариантах для генериков, вынуждены придумывать дизайн, который был обратно совместим с существующим языком и библиотекой классов Java.
Другие языки программирования (например, С++, С#, Ada) позволяют использовать примитивные типы в качестве типов параметров для дженериков. Но обратная сторона этого заключается в том, что реализации таких языков для генериков (или типов шаблонов) обычно влекут за собой генерирование отдельной копии типичного типа для каждой параметризации типа.
1 - Причина, по которой генерики не были включены в Java 1.0, вызвана временным давлением. Они почувствовали, что им нужно быстро выпустить Java-язык, чтобы заполнить новую рыночную возможность, представленную веб-браузерами. Джеймс Гослинг заявил, что он хотел бы включить дженерики, если бы у них было время. Какой бы язык Java выглядел, если бы это произошло, все догадываются.
В коллекциях задан тип, который имеет значение java.lang.Object
. Базисы просто этого не делают.
В java generics реализованы с помощью "Стирание типа" для обратной совместимости. Все общие типы преобразуются в 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" выбирается как тип во время выполнения?
Ответ Объект - это суперкласс всех объектов и может представлять любой пользовательский объект.
Поскольку все примитивы не наследуются от " Object", поэтому мы не можем использовать его как общий тип.
FYI: Project Valhalla пытается решить вышеуказанную проблему.
Согласно Документация по Java, переменные типа общего типа могут быть созданы только с помощью ссылочных типов, а не примитивных типов.
Предполагается, что он появится на Java 10 под Project Valhalla.
В Брайан Гетц документ Состояние специализации
Существует отличное объяснение о причине, по которой общие не поддерживались для примитива. И как это будет реализовано в будущих выпусках Java.
Текущая стираемая Java-реализация, которая создает один класс для всех эталонных экземпляров и не поддерживает примитивные экземпляры. (Это однородный перевод, и ограничение того, что дженерики Java могут распространяться только по ссылочным типам, связано с ограничениями однородного перевода по отношению к набору байт-кода JVM, который использует разные байткоды для операций над ссылочными типами по сравнению с примитивными типами.) Однако стираемые дженерики в Java обеспечивают как поведенческую параметричность (общие методы), так и параметричность данных (исходные и групповые экземпляры родовых типов.)
...
была выбрана однородная стратегия перевода, где общие переменные типа стираются до их границ, поскольку они включены в байт-код. Это означает, что независимо от того, является ли класс общим или нет, он все еще компилируется в один класс с тем же именем и подписи членов которого одинаковы. Безопасность типа проверяется во время компиляции, а среда исполнения неограниченна системой общего типа. В свою очередь это наложило ограничение на то, что дженерики могут работать только над ссылочными типами, поскольку Object является наиболее общим типом, доступным и не распространяется на примитивные типы.