2017-05-05 11 views
3

Ich bin neu in Java Generics, und ich habe Probleme, herauszufinden, seine inneren Abläufe. Wenn Erasures vom Compiler durchgeführt wird, entfernen Sie alle Typparameter, die es in einer .java-Datei gibt, und erzeugen Sie eine normale .class-Datei, die von älteren JVMs verstanden wird. Wie können wir dann eine solche Klasse generisch referenzieren? von anderen Klassen, wissend, dass es die .class-Datei ist, mit der der Java-Compiler arbeitet, wenn wir andere Klassen aus unserem Programm referenzieren? Wie behandelt der Compiler alle Object Referenzen in dieser .class-Datei in Bezug auf die Entscheidung, die ursprünglich Objekt ist und die das Ergebnis von Erasure ist?Generics, Erasures und Bytecode

Antwort

2

Kurz gesagt, Details über Generika und ihre Einschränkungen in Typdeklarationen, Methodensignaturen usw. werden immer noch als Metadaten im Bytecode codiert.

Der Compiler verwendet diese Informationen zur Kompilierzeit, aber die JVM verwendet sie zur Laufzeit nicht. Diese Information ist durch Reflektion zugänglich, und einige Bibliotheken benutzen sie (Hibernate macht das).

Siehe mehr detailed answer here

Edit: ein kleines Experiment in der Praxis spielen zu sehen. In Ergänzung zu @Andy Turners Antwort (die sehr informativ ist: Es zeigt, dass die generische Art Info dort ist), mal sehen, was zur Laufzeit passiert.

Wir prüfen die Klassenstruktur durch Reflexion, und bauen eine Foo<Integer> mit einem String anstelle des Integer:

import java.lang.reflect.Field; 
import java.lang.reflect.Method; 
import java.util.Arrays; 
import java.util.List; 

class Foo<T> { 
    T field; 

    void bar(List<T> list) { 
     T obj = list.get(0); 
     T zip = field; 
    } 

    public static void main(String[] args) throws ReflectiveOperationException { 
     Field field = Foo.class.getDeclaredField("field"); 
     System.out.println("Field:" 
       + "\n - " + field.getType() 
       + "\n - " + field.getGenericType() 
       + "\n - " + field.getAnnotatedType() 
     ); 
     Method method = Foo.class.getDeclaredMethod("bar", List.class); 
     System.out.println("Method:" 
       + "\n - " + Arrays.toString(method.getParameterTypes()) 
       + "\n - " + Arrays.toString(method.getGenericParameterTypes()) 
     ); 
     Foo<Integer> foo = new Foo<>(); 
     // foo.field = "hi"; <- Compile error, incompatible types 
     field.set(foo, "hi"); // 
     // Integer value = foo.field; <- Accepted by compiler, fails at runtime with ClassCastException 
     Object value = foo.field; // OK 
     System.out.println("Value of field: " + value + " (class: " + value.getClass() + ")"); 
    } 
} 

Ergebnis:

Field: 
- class java.lang.Object 
- T 
- sun.re[email protected]5a2e4553 
Method: 
- [interface java.util.List] 
- [java.util.List<T>] 
Value of field: hi (class: class java.lang.String) 
2

Die Generika in Klassen- und Methodensignaturen und Elementvariablen werden nicht gelöscht.

Eine einfache Klasse:

class Foo<T> { 
    T field; 

    void bar(List<T> list) { 
    T obj = list.get(0); 
    T zip = field; 
    } 
} 

dekompilierten:

class Foo<T> { // Still got the <T> here. 
    T field; // Still got the T here. 

    Foo(); 
    Code: 
     0: aload_0 
     1: invokespecial #1     // Method java/lang/Object."<init>":()V 
     4: return 

    void bar(java.util.List<T>); // Still got the <T> here. 
    Code: 
     0: aload_1 
     1: iconst_0 
     // But T has been erased inside the method body. 
     2: invokeinterface #2, 2   // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 
     7: astore_2 
     8: aload_0 
     // And T has been erased when referencing field. 
     9: getfield  #3     // Field field:Ljava/lang/Object; 
     12: astore_3 
     13: return 
} 

erzeugen eine normale .class-Datei, die von älteren JVMs

Diese isn verstanden werden kann Ist das der Fall: wenn yo Wenn Sie Code kompilieren, der Generics verwendet, kann er von JVMs, die keine Generika unterstützen, nicht verstanden werden.

Klassendateien, die auf früheren Versionen kompiliert wurden, sind mit späteren JVMs kompatibel, aber nicht umgekehrt.

+0

Dank. Aber das ist neu für mich: Ich dachte, der resultierende Bytecode wäre derselbe, und Erasures half nur dabei, das zu erhalten. Es ist eine Quellcode Sache - Compiler-Gebiet .., nicht ganz sicher, obwohl ... – Searcherer