Ein gewisser Kontext ist erforderlich, um den Hauptgrund dafür vollständig zu verstehen.
Primitive versus Klassen
Primitive Variablen in Java enthalten Werte (eine Ganzzahl, eine Gleitkomma-Binärzahl mit doppelter Genauigkeit usw.). Da diese Werte unterschiedliche Längen haben können , können die Variablen, die sie enthalten, auch unterschiedliche Längen haben (siehe float
versus double
).
Andererseits enthalten Klassenvariablen Verweise auf Instanzen. Referenzen werden normalerweise in vielen Sprachen als Zeiger (oder ähnlich wie Zeiger) implementiert. Diese Dinge haben in der Regel die gleiche Größe, unabhängig von der Größe der Instanzen sie beziehen sich auf ( Object
, String
, Integer
, usw.).
Diese Eigenschaft von Klassenvariablen macht die darin enthaltenen Referenzen (bis zu einem gewissen Grad) austauschbar . Dies ermöglicht es uns, das zu tun, was wir Substitution nennen : im Großen und Ganzen eine Instanz eines bestimmten Typs als Instanz eines anderen verwandten Typs zu verwenden ( z. B. a String
als Object
.
Primitive Variablen sind weder untereinander noch mit auf die gleiche Weise austauschbarObject
. Der offensichtlichste Grund dafür (aber nicht der einzige Grund) ist ihr Größenunterschied. Dies macht primitive Typen in dieser Hinsicht unpraktisch, aber wir brauchen sie immer noch in der Sprache (aus Gründen, die hauptsächlich auf die Leistung hinauslaufen).
Generika und Typ Löschung
Generische Typen sind Typen mit einem oder mehreren Typparametern (die genaue Anzahl wird als generische Arität bezeichnet ). Beispielsweise verfügt die generische Typdefinition List<T>
über einen Typparameter T
, der Object
(Erzeugen eines konkreten Typs List<Object>
), String
( List<String>
), Integer
( List<Integer>
) usw. sein kann.
Generische Typen sind viel komplizierter als nicht generische. Als sie (nach ihrer ersten Veröffentlichung) in Java eingeführt wurden, beschlossen die Entwickler von Java, generische Typen auf die am wenigsten invasive Weise zu implementieren , um radikale Änderungen an der JVM zu vermeiden und möglicherweise die Kompatibilität mit älteren Binärdateien zu beeinträchtigen: alle konkreten Typen von List<T>
sind in der Tat kompiliert zu (dem binären Äquivalent von) List<Object>
(für andere Typen kann die Grenze etwas anderes sein als Object
, aber Sie bekommen den Punkt). Generische Aritäts- und Typparameterinformationen gehen bei diesem Prozess verloren , weshalb wir sie als Typlöschung bezeichnen .
Die beiden zusammenfügen
Das Problem ist jetzt die Kombination der oben genannten Realitäten: wenn List<T>
wird List<Object>
in allen Fällen, dann T
muss immer ein Typ sein, der direkt zugeordnet werden kannObject
. Alles andere kann nicht erlaubt werden. Da, wie schon gesagt, int
, float
und double
mit nicht austauschbar sind Object
, kann es keine sein List<int>
, List<float>
oder List<double>
(es sei denn , eine wesentlich komplizierter Implementierung von Generika in der JVM existierte).
Aber Java jetzt Angebote mögen Integer
, Float
und Double
die diese Grundelemente in Klasseninstanzen wickeln, so dass sie effektiv substituierbar wie Object
, so ermöglicht generische Typen , um indirekt die Arbeit mit der Primitiven als auch (weil Sie können haben List<Integer>
, List<Float>
, List<Double>
und so weiter).
Das Erstellen eines Integer
aus einem int
, eines Float
aus einem float
und so weiter wird als Boxen bezeichnet . Das Gegenteil heißt Unboxing . Da es Object
unpraktisch ist, Primitive jedes Mal zu boxen, wenn Sie sie verwenden möchten , gibt es Fälle, in denen die Sprache dies automatisch tut - das wird Autoboxing genannt .