Was sind Reified Generics? Wie lösen sie Typlöschprobleme und warum können sie nicht ohne größere Änderungen hinzugefügt werden?

73

Ich habe Neal Gafters Blog zu diesem Thema gelesen und bin in einigen Punkten immer noch unklar.

Warum ist es angesichts des aktuellen Status von Java, der JVM und der vorhandenen Sammlungs-API nicht möglich, Implementierungen der Sammlungs-API zu erstellen, bei denen Typinformationen erhalten bleiben? Könnten diese nicht die vorhandenen Implementierungen in einer zukünftigen Version von Java so ersetzen, dass die Abwärtskompatibilität erhalten bleibt?

Als Beispiel:

List<T> list = REIList<T>(T.Class);

Wo REIList so etwas ist:

public REIList<T>() implements List {
  private Object o;
  private Class klass;

  public REIList(Object o) {
    this.o = o;
    klass = o.getClass();
  }
... the rest of the list implementation ...

Die Methoden verwenden Object o und Class klass, um die Typinformationen abzurufen.

Warum erfordert das Beibehalten allgemeiner Klasseninformationen Sprachänderungen und nicht nur eine Änderung der JVM-Implementierung?

Was verstehe ich nicht?

sal
quelle

Antworten:

40

Der springende Punkt ist, dass reifizierte Generika im Compiler Unterstützung für die Aufbewahrung von Typinformationen haben, während typgelöschte Generika dies nicht tun. AFAIK, der springende Punkt beim Löschen von Typen war in erster Linie die Aktivierung der Abwärtskompatibilität (z. B. konnten JVMs mit niedrigerer Version immer noch generische Klassen verstehen).

Sie können die Typinformationen wie oben explizit in die Implementierung einfügen, dies erfordert jedoch jedes Mal zusätzlichen Code, wenn die Liste verwendet wird, und ist meiner Meinung nach ziemlich chaotisch. In diesem Fall haben Sie immer noch keine Laufzeittypprüfung für alle Listenmethoden, es sei denn, Sie fügen die Prüfungen selbst hinzu. Reified Generics stellen jedoch die Laufzeitarten sicher.

Ghempton
quelle
5
Der für ältere JVMs geschriebene Quell- und Objektcode kann ohne Änderungen auf neuen JVMs ausgeführt werden (Quellcode hat Probleme mit "enum" als Bezeichner, aber das sind keine Generika als solche).
Tom Hawtin - Tackline
1
"JVMs mit niedrigerer Version können immer noch generische Klassen verstehen" <- Ich denke, Sie erhalten einen UnsupportedClassVersionError, wenn Sie versuchen, ein Programm auszuführen, das mit einer neueren Version von JDK in einer älteren Version von JRE / JVM kompiliert wurde. Die Tatsache sollte also das sein, was Tom Hawtin und Richard Gomes gesagt haben, was das Gegenteil ist.
blackr1234
19

Entgegen der Meinung der meisten Java-Entwickler ist es möglich, Informationen vom Typ der Kompilierungszeit beizubehalten und diese Informationen zur Laufzeit abzurufen, obwohl dies sehr eingeschränkt ist. Mit anderen Worten: Java bietet reified Generics auf sehr eingeschränkte Weise .

In Bezug auf die Art der Löschung

Beachten Sie, dass dem Compiler zur Kompilierungszeit vollständige Typinformationen zur Verfügung stehen. Diese Informationen werden jedoch im Allgemeinen absichtlich gelöscht, wenn der Binärcode generiert wird. Dies wird als Typlöschung bezeichnet . Dies geschieht aufgrund von Kompatibilitätsproblemen auf diese Weise: Die Absicht der Sprachentwickler bestand darin, vollständige Quellcodekompatibilität und vollständige Binärcodekompatibilität zwischen Versionen der Plattform bereitzustellen. Wenn es anders implementiert wäre, müssten Sie Ihre Legacy-Anwendungen neu kompilieren, wenn Sie auf neuere Versionen der Plattform migrieren. So wie es gemacht wurde, bleiben alle Methodensignaturen erhalten (Quellcode-Kompatibilität) und Sie müssen nichts neu kompilieren (Binärkompatibilität).

In Bezug auf reified Generics in Java

Wenn Sie Informationen zum Typ der Kompilierungszeit beibehalten müssen, müssen Sie anonyme Klassen verwenden. Der Punkt ist: Im ganz besonderen Fall anonymer Klassen ist es möglich, zur Laufzeit vollständige Informationen zum Typ der Kompilierungszeit abzurufen, was mit anderen Worten bedeutet: reifizierte Generika.

Ich habe einen Artikel zu diesem Thema geschrieben:

http://rgomes-info.blogspot.co.uk/2013/12/using-typetokens-to-retrieve-generic.html

In dem Artikel beschreibe ich, wie unsere Benutzer auf die Technik reagiert haben. Kurz gesagt, dies ist ein obskures Thema, und die Technik (oder das Muster, wenn Sie es vorziehen) erscheint den meisten Java-Entwicklern fremd.

Beispielcode

Der Artikel, den ich oben erwähnt habe, enthält Links zum Quellcode, der die Idee ausübt.

Richard Gomes
quelle
10
Dies hat nichts mit reifizierten Generika zu tun. Ja, Generika in Klassendeklarationen wie die Typen in generischen Parametern, Feldtypen, Typen in Methodensignaturen, Typ der Oberklasse, Typ der einschließenden Klasse innerer Klassen (anonyme Klassen haben nichts Besonderes) werden im Bytecode gespeichert, da andere Klassen Wer diese Klasse verwendet und nur die .class-Datei hat, muss diese Dinge wissen, um Generika durchzusetzen. In der Diskussion zwischen reifizierten und nicht reifizierten Generika wird jedoch über den generischen Typ von Laufzeitobjekten gesprochen , der nichts mit dem zu tun hat, worüber Sie oben sprechen.
Newacct
1
@newacct: Ja, das tut es. Ja, innere Klassen sind unter dem Gesichtspunkt speziell, dass sie Informationen zur Kompilierungszeit beibehalten, die zur Laufzeit abgerufen werden können. Per Definition, wenn Sie zur Laufzeit Informationen zum Typ der Kompilierungszeit aus generischen Klassen abrufen können ... was haben Sie?
Richard Gomes
6
Sie können auch die Informationen zur Kompilierungszeit über die Oberklasse, Superschnittstellen, Felder und Methoden usw. für JEDE Klasse abrufen.
Newacct
10
Nein, Sie rufen einfach den generischen Typparameter ab, mit dem die Oberklasse erweitert wurde. Der anonyme Klassenerstellungsausdruck erstellt eine Instanz einer Klasse, die eine Unterklasse des von Ihnen angegebenen Typs ist. final K k1 = new K<java.lang.Double>() {};ist genau das gleiche wie die lokale Klasseclass Foo extends K<java.lang.Double> {}; final K k1 = new Foo();
Newacct
2
Darum geht es nicht im geringsten. Es geht darum, warum die vom OP beschriebene vereinheitlichte Implementierung eher Sprachänderungen als nur JVM-Änderungen erfordern würde. Ihre Antwort ist über keine.
Marquis von Lorne
14

IIRC (und basierend auf dem Link) sind Java-Generika nur syntaktischer Zucker für die vorhandene Technik, eine Objektsammlung zu verwenden und hin und her zu werfen. Die Verwendung der Java-Generika ist sicherer und einfacher, da der Compiler die Überprüfungen für Sie durchführen kann, um sicherzustellen , dass Sie die Sicherheit vom Typ zur Kompilierungszeit gewährleisten. Die Laufzeit ist jedoch ein ganz anderes Problem.

.NET-Generika hingegen erstellen tatsächlich neue Typen - a List<String>in C # ist ein anderer Typ als a List<Int>. In Java sind sie unter dem Deckmantel dasselbe - a List<Object>. Dies bedeutet, dass Sie, wenn Sie jeweils eine haben, diese zur Laufzeit nicht ansehen und sehen können, als was sie deklariert wurden - nur was sie jetzt sind.

Die reifizierten Generika würden dies ändern und Java-Entwicklern die gleichen Funktionen bieten, die jetzt in .NET vorhanden sind.

Harper Shelby
quelle
2
Ich habe Ihre List <*> in Anführungszeichen gesetzt, damit die Typen angezeigt werden.
David Z
@ David - danke. So oft ich das für jemand anderen getan habe, denkst du, ich würde mich erinnern ...
Harper Shelby
Sie können eine Art von reifizierten Generika mit anonymen Klassen imitieren, dh: Unter diesen Umständen sind zur Laufzeit Typinformationen verfügbar. Obwohl dies möglich ist, ist seine Anwendbarkeit sehr begrenzt und die Technik ist sehr schwierig. Reified Generics ist ein großer Fehler. das ist sicherlich wahr.
Richard Gomes
4

Ich bin kein Experte auf diesem Gebiet, aber so wie ich es verstehe, gehen die Typinformationen beim Kompilieren verloren. Anders als in C ++ verwendet Java kein Vorlagensystem, die Typensicherheit wird vollständig durch den Compiler erreicht. Zur Laufzeit ist eine Liste eigentlich immer eine Liste.

Meiner Meinung nach ist eine Änderung der Sprachspezifikation erforderlich, da die Typinformationen der JVM nicht zur Verfügung stehen, weil sie nicht vorhanden sind .

n3rd
quelle
Dies gilt in den meisten Fällen, außer wenn Sie anonyme Klassen haben. Ich meine: Sie können das Löschen von Typen vermeiden, wenn Sie anonyme Klassen verwenden.
Richard Gomes
Um etwas genauer auf das Thema einzugehen: Java generiert nicht mehrere Kopien Ihres Codes, wie dies C ++ für Vorlagen tut. Java generiert eine einzelne Kopie auf der Angerufenen-Site, auf der Objekte verwendet werden. Obwohl bei anonymen Klassen Informationen zum Laufzeittyp verfügbar sind, ist dies alles, was Sie haben: die Informationen selbst. Der Bytecode zielt nicht auf die tatsächlichen Typen ab, die Sie zur Laufzeit übergeben.
Richard Gomes