2017-09-27 1 views
5

Ich untersuche, wie Bibliotheken, die Proxy-Objekte erstellen, insbesondere ich möchte verstehen, wie sie Typen von deklarierten Methoden abrufen. Zum Beispiel beliebte Bibliothek für Android - Retrofit:Wie Typ Löschen funktioniert

interface MyService { 

    @GET("path") 
    Call<MyData> getData(); 
} 

Ich war verwirrt - wie es möglich ist, von dieser Schnittstelle genau MyData Klasse nicht roh Objekt zu bekommen? Aufgrund meines Verständnisses löscht Type Erase alle Informationen, die in generische Klammern gesetzt wurden.

Ich schrieb einigen Test-Code und überraschend für mich ist es einfach Art von solchen Code zu erhalten:

@org.junit.Test 
public void run() { 
    Method[] methods = Test.class.getDeclaredMethods(); 
    Method testMethod = methods[0]; 
    System.out.println(testMethod.getReturnType()); 
    ParameterizedType genericType = ((ParameterizedType) testMethod.getGenericReturnType()); 
    Class<Integer> clazz = (Class<Integer>) genericType.getActualTypeArguments()[0]; 
    System.out.println(clazz); 
} 

interface Test { 
    List<Integer> test(); 
} 

Es sieht ein bisschen schmutzig, aber es funktioniert und Druck Integer. Das bedeutet, dass wir Typen in Runtime haben. Auch ich habe über einen anderen schmutzigen Trick mit anonymen Klassen lesen:

System.out.println(new ArrayList<Integer>().getClass().getGenericSuperclass()); 

druckt roh AbstractList<E> während dieser Code

System.out.println(new ArrayList<Integer>() { }.getClass().getGenericSuperclass()); 

druckt ArrayList<Integer>.

Und es ist nicht das letzte, was mich verwirrt. In Kotlin sind dort verdinglichten Generika, aus denen wie ein Hack in der Kompilierung aussieht, aber wir können einfach Klasse von Generika erhalten:

inline fun <reified T> test() { 
    print(T::class) 
} 

Und jetzt bin ich völlig mit Typ-Löschmechanismus verwirrt.

  1. Kann jemand erklären, warum manchmal Informationen hält und manchmal nicht?
  2. Warum Generika in Java nicht normal implementiert ist? Ja, ich habe gelesen, dass es die Kompatibilität mit früheren Versionen brechen könnte, aber ich möchte verstehen, wie. Warum bricht generischer Rückgabetyp nichts außer new ArrayList<Integer>?
  3. Warum haben anonyme Klassen einen generischen Typ und werden nicht vom Typ gelöscht?

Aktualisiert: 4. Wie Generika in Kotlin arbeitet sie verdinglicht und warum so coole Sache nicht in Java implementiert werden können?

Here erklärt ziemlich klar darüber, wie vergiftete Generika arbeiten. @Mibac

Sie können nur in Kombination mit einer Inline-Funktion verdinglichten verwenden. Solch eine eine Funktion macht den Compiler den Bytecode der Funktion zu jedem Ort kopieren, wo die Funktion verwendet wird (die Funktion ist "inline"). Wenn Sie eine Inline-Funktion mit verdichtetem Typ aufrufen, kennt der Compiler den tatsächlichen Typ, der als Typargument verwendet wird, und ändert den generierten Bytecode, um die entsprechende Klasse direkt zu verwenden. Deshalb Aufrufe wie myVar ist T myVar ist String, wenn der Typ Argument String, im Bytecode und zur Laufzeit waren.

+3

[Wie 'vereidigt' funktioniert] (https://Stackoverflow.com/a/45952201/3533380) – Mibac

Antwort

3

Kann jemand erklären, warum manchmal hält es Informationen und manchmal nicht der Fall ist?

Auf Jvm Ebene gibt es ein Signature attribute dass:

eine Signatur Aufzeichnungen (§4.7.9.1) für eine Klasse, Schnittstelle, Konstruktor, Methode oder Feld, dessen Erklärung in der Programmiersprache Java Anwendungen Typ Variablen oder parametrisierte Typen.

Einer der Gründe, warum Sie es haben möchten ist, dass zum Beispiel Compiler tatsächliche Art der Argumentation eines some_method, die von vorkompilierte some.class kommt wissen muss (das kommt von Dritten some.jar). In diesem Szenario unter der Annahme, dass ObjectObject Annahmen Annahmen, mit denen some.class wurde kompiliert wurde, und Sie können nicht some_method in einer typsicheren Weise aufrufen.

Warum haben anonyme Klassen einen generischen Typ und werden nicht vom Typ gelöscht?

Wenn Sie anrufen:

System.out.println(new ArrayList<Integer>().getClass().getGenericSuperclass()); 

... nichts in Bezug auf die Jvm Klassen definiert wird, während dies:

System.out.println(new ArrayList<Integer>() { }.getClass().getGenericSuperclass()); 

... definiert eigentlich Klasse auf der jvm level, albiete anonym auf der java sprachstufe.

Dies ist der Grund, warum Reflexion für diese Fälle unterschiedliche Ergebnisse liefert.

Warum Generika in Java nicht auf normale Weise implementiert werden? Ja, ich habe gelesen, dass es die Kompatibilität mit früheren Versionen brechen könnte, aber ich möchte verstehen, wie. Warum bricht generischer Rückgabetyp nichts außer neuen ArrayListdoes?

Wie verdinglichten Sie arbeitet Generika in Kotlin und warum so coole Sache nicht in Java implementiert werden?

Ich glaube nicht, dass Sie eine objektive Antwort geben können, die nicht auf Meinungen basiert. Außerdem würde ich vorschlagen, den Umfang Ihrer Frage zu spalten oder einzugrenzen.