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