Чому Java Generics не підтримують примітивні типи?


236

Чому дженерики на Java працюють з класами, а не з примітивними типами?

Наприклад, це добре працює:

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

але це заборонено:

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

1
int i = (int) новий об’єкт (); компілює просто чудово, хоча.
Сахін Верма

Відповіді:


243

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);

Отже, все, що використовується як дженерики, повинно бути конвертоване в Object (у цьому прикладі get(0)повертає an Object), а примітивні типи - ні. Тому їх не можна використовувати в дженеріках.


8
@DanyalAytekin - Насправді, дженерики Java взагалі НЕ обробляються як шаблони C ++ ...
Stephen C

20
Чому компілятор Java не може також встановити вікно примітивного типу до його використання? Це має бути можливо, правда?
vrwim

13
@vrwim - Це можливо. Але це був би просто синтаксичний цукор. Справжня проблема полягає в тому, що дженеріки Java з коробчастими примітивами є відносно дорогими як у часі, так і в просторі порівняно з моделлю C ++ / C # ... де використовується фактичний примітивний тип.
Стівен С

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

4
@Ced повністю погоджуються з тим, що це поганий дизайн, який нескінченно шкодить початківцям ано професіоналам
MauganRa

37

У Java дженерики працюють так, як вони роблять ... принаймні частково ... тому, що вони були додані до мови через кілька років після розробки мови 1 . Мовленнєві дизайнери обмежувались у своїх можливостях для дженерики тим, що придумали дизайн, який був би сумісний із існуючою мовою та бібліотекою класів Java .

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


1 - Причина, що дженерики не були включені в Java 1.0, була через тиск у часі. Вони відчували, що їм доведеться швидко випустити мову Java, щоб заповнити нову ринкову можливість, представлену веб-браузерами. Джеймс Гослінг заявив, що хотів би включити дженерики, якби у них був час. Як би виглядала мова Java, якби це сталося - це хтось здогадається.


11

У 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()

Тепер питання полягає в тому, чому "Об'єкт" вибирається як тип під час виконання?

Відповідь: Об'єкт є надкласовим рівнем усіх об'єктів і може представляти будь-який визначений користувачем об’єкт.

Оскільки всі примітиви не успадковують " Об'єкт ", тому ми не можемо використовувати його як загальний тип.

FYI: Проект Valhalla намагається вирішити вищезазначене питання.


Плюс 1 для належної номенклатури.
Дражен Беловук,

7

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


26
Я думаю, тут питання "чому". Чому для генерики потрібні об'єкти? Здається, що консенсус полягає в тому, що це менше вибору дизайну і більше прихильності до відсталої сумісності. На моїх очах, якщо дженерики не можуть впоратися з примітивами, це дефіцит функціональності. Як відомо, все, що стосується примітивів, має бути написано для кожного примітиву: замість компаратора <t, t> ми маємо Integer.compare (int a, int b), Byte.compare (байт a, байт b) тощо. Це не рішення!
Джон П

1
Так, дженерики над примітивними типами були б обов'язковою особливістю. Ось посилання на пропозицію до неї openjdk.java.net/jeps/218
ворона

5

Відповідно до документації Java , змінні загального типу можна інстанціювати лише за допомогою посилальних типів, а не примітивних типів.

Це має з'явитися на Java 10 в рамках проекту Valhalla .

У статті Брайана Геца про стан спеціалізації

Існує відмінне пояснення про причину, через яку родові не підтримували примітиву. І як це буде реалізовано в майбутніх випусках Java.

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

...

була обрана гомогенна стратегія перекладу, де змінні загального типу стираються до їх меж, оскільки вони включаються в байт-код. Це означає, що незалежно від того, клас є загальним чи ні, він все ще компілюється до одного класу з тим самим іменем і підписи членів якого однакові. Безпека типу перевіряється під час компіляції, а час виконання не визначається системою загального типу. У свою чергу, це наклало обмеження, що дженерики можуть працювати лише над референтними типами, оскільки Об'єкт є найбільш загальним наявним типом, і він не поширюється на примітивні типи.


0

Створюючи об'єкт, ви не можете замінити примітивний тип параметром типу. Що стосується цього обмеження, це питання реалізації компілятора. Примітивні типи мають власні інструкції щодо байт-коду для завантаження та зберігання у стек віртуальної машини. Тож неможливо скласти примітивні дженерики в ці окремі шляхи байт-коду, але це ускладнить компілятор.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.