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


236

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

Например, это прекрасно работает:

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

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

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

1
int i = (int) new Object (); компилируется просто отлично, хотя.
Сачин Верма

Ответы:


243

Обобщения в 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), а примитивные типы - нет. Таким образом, они не могут быть использованы в дженериках.


8
@DanyalAytekin - На самом деле, дженерики Java вообще не обрабатываются как шаблоны C ++ ...
Стивен C

20
Почему компилятор Java также не может блокировать примитивный тип до того, как он привыкнет? Это должно быть возможно правильно?
vrwim

13
@vrwim - Возможно, это возможно. Но это был бы просто синтаксический сахар. Реальная проблема заключается в том, что дженерики Java с коробочными примитивами относительно дороги как во времени, так и в пространстве по сравнению с моделью C ++ / C # ... где используется фактический тип примитива.
Стивен С

6
@MauganRa Да, я знаю, что могу :) Я стою на своем месте, что это ужасный дизайн, хотя. Надеюсь, это будет исправлено в java 10 (или так я слышал), а также в функциях более высокого порядка. Не цитируйте меня по этому поводу.
Сед

4
@Ced полностью согласен с тем, что это плохой дизайн, который бесконечно ранит как начинающих, так и профессионалов
MauganRa

37

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

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


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


11

В 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 пытается решить вышеуказанную проблему.


Плюс 1 за правильную номенклатуру.
Дражен Беловук

7

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


26
Я думаю, что вопрос здесь «почему». Почему для дженериков требуются объекты? Похоже, консенсус заключается в том, что это не столько выбор дизайна, сколько уступка обратной совместимости. На мой взгляд, если дженерики не могут обрабатывать примитивы, это недостаток функциональности. В сущности, все, что включает в себя примитивы, должно быть написано для каждого примитива: вместо Comparator <t, t> у нас есть Integer.compare (int a, int b), Byte.compare (байт a, байт b) и т. Д. Это не решение!
Джон П,

1
Да, дженерики над примитивными типами являются обязательной функцией. Вот ссылка на предложение для него openjdk.java.net/jeps/218
ворона

5

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

Это должно прийти на Java 10 в рамках проекта Valhalla .

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

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

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

...

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


0

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.