Guten Tag,
mich beschäfftigt zur Zeit ein Thema sehr.
Und zwar wie die Generics in Java umgesetzt sind. Ich habe gelesen ,dass in Java die Generics homogen realisert werden.
So fern ich es verstanden habe erzeugt Java für jeden gernerischen Typen genau eine Klasse in die generischen Typen durch Object ersetzt werden.
Dies führt zu einer Typlöschung.
Leider verstehe ich nicht ganz was mit der erzeugung von nur einer generischen Klasse gemeint ist oder wieso es zu einer Typlöschung kommt.
Woher weiß der Compiler zur Laufzeit was nun der spezifische Typ ist wenn dieser von zb. <String> zu <Object> ersetzt wurde.
Und wie würde eine Heterogene Realisierung aussehen ?
Ich freu mich über jede Antwort
Ich denke hier ist dein Problem. Der Compiler macht ja nichts zur Laufzeit, er kompiliert einfach nur - also javac. Zur Laufzeit ist der Compiler längst fertig und dann gibt es den generischen Typ nicht mehr.
Type erasure: https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
Ich denke hier ist dein Problem. Der Compiler macht ja nichts zur Laufzeit, er kompiliert einfach nur - also javac. Zur Laufzeit ist der Compiler längst fertig und dann gibt es den generischen Typ nicht mehr.
Type erasure: https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
Danke für die Antwort
Leider wurde es mir noch immer nicht ganz klarer. Nur die Frage kann ich nun korekter stellen : Woher weiß der Compiler was nun der spezifische Typ ist ?
Der Compiler arbeitet mit dem Source und im Source steht der spezifische Typ. Weiterhin erzeugt er den Bytecode und in dem wird der spezifische Typ entfernt. Das ist in dem Link zu type erasure beschrieben.
Zur Laufzeit (Das ist nicht der Java Compiler sondern die Java Laufzeitumgebung!) wird nur mit dem Bytecode gearbeitet bei dem es dann den spezifischen Typ so nicht mehr gibt.
Der Compiler arbeitet mit dem Source und im Source steht der spezifische Typ. Weiterhin erzeugt er den Bytecode und in dem wird der spezifische Typ entfernt. Das ist in dem Link zu type erasure beschrieben.
Zur Laufzeit (Das ist nicht der Java Compiler sondern die Java Laufzeitumgebung!) wird nur mit dem Bytecode gearbeitet bei dem es dann den spezifischen Typ so nicht mehr gibt.
Danke für die ausführliche Antwort
Es zerbricht mir tatsächlich bisschen den Kopf. Also ist die Typ löschung im Grunde keine Löschung sonder ein Ersetzen durch den Typ Object. Gelöscht wird der Typ im Grunde ja nicht weil dieser sich ja noch für eventuell benötigte Casts gemerkt wird oder ?
Im ersten Link ist in Kürze sehr genau gesagt, was mit type erasure gemacht wird.
A) type Parameter werden ersetzt (und damit verschwindet der original Type … das kann also durchaus auch als Löschen bezeichnet werden. Das wäre ja auch die Übersetzung von erasure.)
B) es werden - wo notwendig - Type casts eingefügt
C) bridge methods werden eingefügt bei der Vererbung von generischen Typen
Zum Glück ist nicht der Tobias derjenige, der alles festlegt.
a) Die Wortwahl wurde über die Java Language Specification festgelegt. Und da ist in 4.6 "Type Erasure" festgelegt worden. Ob nun der englische Begriff "erasure" passend ist oder nicht mag man gerne diskutieren, aber das führt zu nichts. Wir haben eine klare Begriffsbestimmung und diesen Begriff nutzt man. Und wenn man dann diesen Begriff übersetzen will um einen Deutschen Begriff zu nutzen, dann wäre die Übersetzung eher Löschung oder Radierung und eben nicht Substituierung, Ersetzung oder ähnliches.
b) @John_Sace nutzt als Anfänger eher Umgangssprache. Daher sollte die Verwendung des Begriffes Löschung also auch aus Deiner Sicht ok sein. Danke für diese Bestätigung und Einsicht.
Aber auch für Dich ist wichtig: Das ist nicht der Compiler! "Woher weiß der Compiler zur Laufzeit was nun der spezifische Typ" - der Compiler weiss zur Laufzeit gar nichts, denn der (Java) Compiler ist nur zur Compilezeit aktiv! Und wenn man den JIT betrachtet: Der JIT übersetzt lediglich Bytecode weiter - den interessiert kein spezifischer Typ.
Und nein - dynamic binding hat mit Generics erst einmal nichts zu tun! Das wird z.B. deutlich, wenn Du mal versuchst in einer Klasse die folgenden zwei Methoden zu haben:
Auf Grund der type erasure haben die Methoden die gleiche Signatur was dann auch die Fehlermeldung direkt aussagt: name clash: someMethod(java.util.List<java.lang.String>) and someMethod(java.util.List<java.lang.Integer>) have the same erasure
Das liegt an der Type Erasure, denn aus List<String> ist einfach ein List<Object>. Der Typparameter ist also "entfernt" worden. (Und hier wird mir auch gerade klar, wieso da Type Erasure als Name gewählt wurde. Der Typparameter wird entfernt. Die generische Klasse List<> verhält sich dann unter dem Strich so, wie die Klasse List vor Einführung der Generics. Aber das nur so ganz am Rande. Ich mag damit falsch liegen, aber sowas würde Sinn machen.)
Aber natürlich lässt sich der Code nicht mehr Übersetzen. Auch wenn der Bytecode funktionieren würde. Aber bei einer Übersetzung erhält man nun natürlich:
Code:
konrad@MBP-von-Konrad java % javac TestApp.java TestList.java
TestApp.java:3: Fehler: Inkompatible Typen: int kann nicht in String konvertiert werden
TestList.content.add(17);
^
Hinweis: Einige Meldungen wurden vereinfacht. Wiederholen Sie die Kompilierung mit -Xdiags:verbose, um die vollständige Ausgabe abzurufen
1 Fehler
konrad@MBP-von-Konrad java %
Das ist also eine kleine Spielerei, die evtl. bei dem Verständnis etwas hilft. Denn der ByteCode entspricht unter dem Strich dem:
Aber da der Compiler Generics natürlich kennt und das für Typechecks verwendet wird, gibt es nun eine Warnung, dass der Aufruf unchecked ist. Daher müsste man die Warnung unterdrücken, wenn man die List ohne Generic ansprechen möchte:
Das aber nur als Hinweis um hier etwas damit zu spielen, was da passiert. Selbstverständlich gibt man die Typ Parameter an und sorgt so dafür, dass der Compiler die Typen prüfen kann!
Du hast ganz klar eine Frage beantwortet. Und das ist definitiv falsch. Kannst Du selbst nachsehen, aber ich kann es Dir auch schnell als Bild einbinden:
Somit lege ich Dir keine Sachen in den Mund. Wenn jemand fragt: "Was ist 2 + 2" und Du antwortest 5. Dann wird diese Antwort falsch sein. Da kannst Du noch so sehr behaupten, dass Du auf eine andere Frage wie 2 + 3 antworten wolltest. Du hast auf die Frage "was ist 2 + 2" mit 5 geantwortet und das ist schlicht nicht "Sachen in den Mund legen". Du magst da etwas anderes gelesen oder verstanden haben. Aber dann solltest Du an deiner Kommunikation arbeiten.
Aber das werde ich nicht weiter vertiefen. Solche sinnlosen Diskussionen hatten wir schon viel zu oft und daher ist klar, wie sinnlos so eine Diskussion ist.
Evtl. zu genau diesem Punkt paar Worte mehr, da dies der wirklich wichtige Punkt ist:
Durch Generics bekommen wir zur Compilezeit eine Typüberprüfung! Das ist also speziell für den Compiler gemacht und gibt uns beim Übersetzen unseres Codes eine deutlich größere Typsicherheit.
Das ist der wichtige Punkt, der hier verstanden werden sollte.
Es geht um einen "extra layer", also einer zusätzlichen Schicht, die auf das bisherige Java gelegt wurde. Vorher war alles bei sowas einfach ein Object. Durch diese Schicht haben wir nun im Code bei uns eine Typsicherheit und unser Code mit Generics wird sozusagen in etwas umgesetzt, dass ohne Generics auskommt.
Aber hier gilt: Das ist eine stark vereinfachte Sicht! Wir haben hier jetzt speziell den letzten Punkt bei der Beschreibung von type erasure komplett ignoriert!
Wichtig ist hier also der Unterschied zu z.B. Templates bei C++. Man erstellt in C++ ein Template und wenn das genutzt wird, dann entsteht tatsächlich eine konkrete Implementierung von dem Template. Also auch wenn da teilweise von "Generics" gesprochen wird (z.B. bei Generics in C++ - GeeksforGeeks), ist das doch eine komplett andere Sache (und man sollte da von Templates reden - das ist ja auch das Keyword).
Möglicherweise kann man type erasure besser verstehen wenn man davon ausgeht, das in Java eig. alles ein Object ist(primitive kommen hier ja nicht in Frage). Wenn ich also den konkreten Typen entferne dann ist es trotzdem ein Object. Also man muss den Typen nicht ändern um auf Object zu kommen. Implizit ist es das immer.
Du solltest auch davon ausgehen, das Generics einfach ein später Zusatz in Java sind. Früher (war nicht alles besser) gab es z.B. nur List ohne einen generischen Typen. In der List waren immer nur Object's. Heute ist das im Prinzip immer noch so. Nur das man für den Programmierer ein Hilfsmittel im Compiler geschaffen hat. List war quasi nicht typisiert - was man aber in Java, als typisierte Sprache, haben möchte. Man hat früher nicht nur eine Unmenge Cast's durchführen müssen, sondern musste auch selbst dafür sorgen, dass man immer nur passende Objekte in eine List gefüllt hat. Jetzt ist der Compiler (und damit indirekt die IDE) weitgehend in der Lage zu erkennen welcher Typ in eine Liste passt und auch wieder herauskommt.