2016-12-01 1 views
1

Ich bin beim Lernen von Java auf eine seltsame Sache gestoßen. Betrachten Sie das folgende Programm:getClass() gibt den abgeleiteten Klassennamen nach dem Upcasting zurück

public class GetClassNameInheritance { 
    public static void main(String[] args) {   
     Employee e = new Employee(); 
     Person p = (Person) e; 
     System.out.println(p.getClass()); 
    } 
} 

class Person { 

} 

class Employee extends Person { 

} 

ich den Ausgang erwarten sein Person wegen der Besetzung, aber es ist Employee! Ehrlich gesagt bin ich ratlos und finde keine Erklärung. Das offizielle Oracle tutorial erwähnt dieses Verhalten nicht, und die docs scheinen zu kurz, um hilfreich zu sein. Ich kann aus anderen StackOverflow-Beispielen erkennen, dass dies etwas mit "Laufzeitklasse" zu tun hat, aber ich verstehe die zugrundeliegende Idee nicht. Kann mir jemand erklären, was hier vor sich geht?

Antwort

2

In Java ändert Typcasting den Objekttyp nicht. Einmal ein Employee, ist Ihr Objekt immer ein Employee. Typumwandlungen funktionieren nicht wie in anderen Sprachen wie C/C++.

Obwohl die Referenz vom Typ Person ist, bezieht sie sich tatsächlich auf ein Objekt vom Typ Employee im JVM-Heap. Wenn Sie die Methode p.getClass() aufrufen, ruft die Referenz wiederum die Methode für das Objekt und nicht für die Referenz auf. Das ist der Grund, warum Sie Employee in Ihrer Ausgabe sehen.

Die einzige Art von Umwandlungen, die funktionieren würde, ist diejenige, die Sie auf Primitiven ausführen können.

int x = (int)2.5f;

Der Rückgabewert wird Typ umgewandelt werden in int.

Sie können mehr über Typ Abgüsse und Umbauten lesen Sie hier:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html

http://www.wideskills.com/java-tutorial/java-object-typecasting

+0

Hmmm, sehr interessant. Wenn ich also "(Person) e" sage, wählt die JVM zur Laufzeit nur diejenigen Attribute von "Employee" aus, die der Schnittstelle "Person" entsprechen. Sehr interessant, in der Tat! – dotslash

+1

Das ist genau das, was es tut. Zur Kompilierzeit überprüft es nur die Hierarchie. Zur Laufzeit tut es das tatsächlich. Wenn zur Laufzeit die Objekte nicht übereinstimmen, erhalten Sie eine Class Cast-Ausnahme. – anacron

0

Wenn Sie die Superklasse der aktuellen Klasse wissen wollen, können Sie

p.getClass().getSuperclass() //returns person. 

Aus der Dokumentation

getClass() gibt immer die Laufzeitklasse dieses Objekts zurück. Das zurückgegebene Klassenobjekt ist das Objekt, das durch statische synchronisierte Methoden der dargestellten Klasse gesperrt ist.

Und eine Laufzeitklasse ist eine Instanz, die mit dem Konstruktoraufruf instanziiert wurde.

Eigentlich, wenn Sie sehen werden, hier Person p = (Person) e; die Besetzung ist nicht erforderlich, können Sie immer eine abgeleitete Klasse Objekt zu einer Basisklasse zuweisen.

Für Beispiel

List<String> list = new ArrayList<>(); 

Above keinen Fehler werfen.

1

Na mal schauen, was passiert, wenn die Hauptmethode kompiliert wird:

 0: new   #2     // class Employee 
    3: dup   
    4: invokespecial #3     // Method Employee (Constructor) 
    7: astore_1       // Save local variable "e" 
    8: aload_1       // Load local variable "e" 
    9: astore_2       // Save to local variable "p" 
    10: getstatic  #4     // Field System.out PrintStream; 
    13: aload_2       // Load local variable "p" 
    14: invokevirtual #5     // Method Object.getClass() 
    17: invokevirtual #6     // Method PrintStream.println(Object) 

Und wenn wir bei einem Decompiler Interpretation dieses Bytecode sehen waren:

public static void main(String[] args) { 
    Employee e = new Employee(); 
    System.out.println(e.getClass()); 
} 

Der Compiler hat „optimiert " Der Code, der dazu führt, dass Ihre Besetzung entfernt wird, da sie für unnötig erachtet wurde. Anacrons Antwort erklärt, warum der Compiler es für unnötig hielt.

+0

Vielen Dank für den JVM- und Decompiler-Code! Es hat meinem Verständnis eine ganz neue Dimension hinzugefügt. :-) – dotslash

Verwandte Themen