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>();
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>();
Antworten:
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.
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.
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.
Die Sammlungen sind so definiert, dass sie einen Typ erfordern, von dem abgeleitet wird java.lang.Object
. Die Basetypen machen das einfach nicht.
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.
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.