Warum unterstützen Java Generics keine primitiven Typen?

236

Warum arbeiten Generika in Java mit Klassen, aber nicht mit primitiven Typen?

Dies funktioniert beispielsweise einwandfrei:

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

das ist aber nicht erlaubt:

List<int> bar = new ArrayList<int>();
Sgokhales
quelle
1
int i = (int) neues Objekt (); kompiliert aber ganz gut.
Sachin Verma

Antworten:

243

Generics in Java sind ein Konstrukt zur Kompilierungszeit. Der Compiler wandelt alle generischen Verwendungen in Casts des richtigen Typs um. Dies dient dazu, die Abwärtskompatibilität mit früheren JVM-Laufzeiten aufrechtzuerhalten.

Dies:

List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);

wird in (grob) verwandelt:

List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);

Alles, was als Generika verwendet wird, muss also in Object konvertierbar sein (in diesem Beispiel wird get(0)ein zurückgegeben Object), die primitiven Typen jedoch nicht. Sie können also nicht in Generika verwendet werden.

thecoop
quelle
8
@DanyalAytekin - In der Tat werden Java-Generika überhaupt nicht wie C ++ - Vorlagen behandelt ...
Stephen C
20
Warum kann der Java-Compiler den primitiven Typ nicht auch boxen, bevor er verwendet wird? Das sollte doch möglich sein, oder?
vrwim
13
@vrwim - Es könnte möglich sein. Aber es wäre nur syntaktischer Zucker. Das eigentliche Problem ist, dass Java-Generika mit Boxed-Primitiven im Vergleich zum C ++ / C # -Modell, bei dem der eigentliche Primitivtyp verwendet wird, zeitlich und räumlich relativ teuer sind.
Stephen C
6
@MauganRa Ja, ich weiß, ich kann :) Ich stehe zu meinem Standpunkt, dass dies ein schreckliches Design ist. Hoffentlich wird es in Java 10 (oder wie ich gehört habe) und auch Funktionen höherer Ordnung behoben. Zitiere mich nicht dazu.
Ced
4
@Ced voll und ganz zustimmen, dass es schlechtes Design ist, das Anfänger und Profis gleichermaßen endlos schmerzt
MauganRa
37

In Java funktionieren Generika so, wie sie es tun ... zumindest teilweise ..., weil sie einige Jahre nach dem Entwurf der Sprache zur Sprache hinzugefügt wurden 1 . Die Sprachdesigner wurden gezwungen in ihren Optionen für Generika durch mit einem Entwurf zu kommen haben , die rückwärts kompatibel mit der bestehenden Sprache war und die Java - Klassenbibliothek .

Andere Programmiersprachen (z. B. C ++, C #, Ada) erlauben die Verwendung primitiver Typen als Parametertypen für Generika. Die Kehrseite davon ist jedoch, dass die Implementierung von Generika (oder Vorlagentypen) in solchen Sprachen typischerweise die Erzeugung einer eigenen Kopie des generischen Typs für jede Typparametrisierung beinhaltet.


1 - Der Grund, warum Generika nicht in Java 1.0 enthalten waren, war der Zeitdruck. Sie hatten das Gefühl, dass sie die Java-Sprache schnell veröffentlichen mussten, um die neuen Marktchancen zu nutzen, die Webbrowser bieten. James Gosling hat erklärt, dass er gerne Generika aufgenommen hätte, wenn sie die Zeit gehabt hätten. Wie die Java-Sprache ausgesehen hätte, wenn dies geschehen wäre, ist unklar.

Stephen C.
quelle
11

In Java werden Generika implementiert, indem "Type Erasure" verwendet wird, um die Abwärtskompatibilität zu gewährleisten. Alle generischen Typen werden zur Laufzeit in Object konvertiert. beispielsweise,

public class Container<T> {

    private T data;

    public T getData() {
        return data;
    }
}

wird zur Laufzeit als angezeigt

public class Container {

    private Object data;

    public Object getData() {
        return data;
    }
}

Der Compiler ist dafür verantwortlich, die richtige Besetzung bereitzustellen, um die Typensicherheit zu gewährleisten.

Container<Integer> val = new Container<Integer>();
Integer data = val.getData()

wird werden

Container val = new Container();
Integer data = (Integer) val.getData()

Die Frage ist nun, warum "Objekt" zur Laufzeit als Typ ausgewählt wird.

Antwort ist Objekt ist eine Oberklasse aller Objekte und kann jedes benutzerdefinierte Objekt darstellen.

Da alle Primitiven nicht von „erben Objekt “ , so dass wir es nicht als generische Art verwenden können.

Zu Ihrer Information: Das Projekt Valhalla versucht, das oben genannte Problem anzugehen.

Piyush Sagar
quelle
Plus 1 für die richtige Nomenklatur.
Drazen Bjelovuk
7

Die Sammlungen sind so definiert, dass sie einen Typ erfordern, von dem abgeleitet wird java.lang.Object. Die Basetypen machen das einfach nicht.

ZeissS
quelle
26
Ich denke, die Frage hier ist "warum". Warum benötigen Generika Objekte? Der Konsens scheint zu sein, dass es weniger eine Designentscheidung ist, sondern vielmehr die Abwärtskompatibilität beeinträchtigt. In meinen Augen ist das ein Funktionsdefizit, wenn Generika nicht mit Grundelementen umgehen können. So wie es aussieht, muss alles, was Primitive beinhaltet, für jedes Primitiv geschrieben werden: Anstelle von Comparator <t, t> haben wir Integer.compare (int a, int b), Byte.compare (Byte a, Byte b) usw. Das ist keine Lösung!
John P
1
Ja, Generika über primitive Typen sind ein Muss. Hier ist ein Link zu einem Vorschlag dafür openjdk.java.net/jeps/218
Crow
4

Gemäß der Java-Dokumentation können generische Typvariablen nur mit Referenztypen instanziiert werden, nicht mit primitiven Typen.

Dies soll in Java 10 unter Project Valhalla geschehen .

In Brian Goetz Artikel über den Stand der Spezialisierung

Es gibt eine ausgezeichnete Erklärung für den Grund, aus dem Generika für primitive nicht unterstützt wurden. Und wie es in zukünftigen Versionen von Java implementiert wird .

Javas derzeit gelöschte Implementierung, die eine Klasse für alle Referenzinstanziierungen und keine Unterstützung für primitive Instanziierungen erzeugt. (Dies ist eine homogene Übersetzung, und die Einschränkung, dass Javas Generika nur über Referenztypen reichen können, ergibt sich aus den Einschränkungen der homogenen Übersetzung in Bezug auf den Bytecodesatz der JVM, die unterschiedliche Bytecodes für Operationen an Referenztypen gegenüber primitiven Typen verwendet.) Gelöschte Generika in Java bieten jedoch sowohl Verhaltensparametrizität (generische Methoden) als auch Datenparametrizität (Roh- und Platzhalterinstanziierungen generischer Typen).

...

Es wurde eine homogene Übersetzungsstrategie gewählt, bei der generische Typvariablen bis zu ihren Grenzen gelöscht werden, wenn sie in den Bytecode integriert werden. Dies bedeutet, dass eine Klasse, unabhängig davon, ob sie generisch ist oder nicht, immer noch zu einer einzelnen Klasse mit demselben Namen kompiliert wird und deren Mitgliedssignaturen identisch sind. Die Typensicherheit wird zur Kompilierungszeit überprüft, und die Laufzeit wird vom generischen Typsystem nicht eingeschränkt. Dies führte wiederum zu der Einschränkung, dass Generika nur über Referenztypen arbeiten können, da Object der allgemeinste verfügbare Typ ist und sich nicht auf primitive Typen erstreckt.

vinS
quelle
0

Beim Erstellen eines Objekts können Sie den Typparameter nicht durch einen primitiven Typ ersetzen. Was das Warum dieser Einschränkung betrifft, handelt es sich um ein Compiler-Implementierungsproblem. Primitive Typen haben ihre eigenen Bytecode-Anweisungen zum Laden und Speichern auf dem Stapel der virtuellen Maschine. Es ist also nicht unmöglich, primitive Generika in diese separaten Bytecode-Pfade zu kompilieren, aber dies würde den Compiler kompliziert machen.

Atif
quelle