Andreas Solymosi

Casting this in Java

Ein nicht seltenes Problem: Wie können verdeckte Attribute und überladene Methoden der Ober-Oberklasse erreicht werden? Wenn die Attribute nicht verdeckt und die Methoden nicht überladen werden, können sie bei Namen genannt werden. Für die Attribute und Methoden der Oberklasse steht super. zur Verfügung. Aber es gibt kein super.super. – auf die Ober-Oberklasse gibt es keinen Zugriff.

Glücklicherweise kann man this konvertieren (casting) und damit auf die Attribute der Ober-Oberklasse zugreifen:

public class A {
  protected String attribut = "A";
  protected void methode() {
     System.out.print(attribut); }  } // wie erwartet: A
class B extends A {
  protected String attribut = "B"; // verdeckt A.attribut
  protected void methode() {
     System.out.print(attribut); // wie erwartet: B
     System.out.print(super.attribut); // wie erwartet: A
     super.methode(); } } // wie erwartet: A
class C extends B {
  protected String attribut = "C"; // verdeckt B.attribut
  protected void methode() {
     System.out.print(attribut); // wie erwartet: C
     System.out.print(super.attribut); // wie erwartet: B
     System.out.print(((A)this).attribut); // A.attribut !!!
     super.methode(); // B.methode() -> BAA
    
try {
       ((A)this).methode(); /* A.methode()? nein, C.methode() -> endlose Rekursion */
     } catch (StackOverflowError e) {
       System.out.println("Endlose Rekursion"); } }
 
public static void main(String[] args) {
     System.out.println("erwartet: A");
     new A().methode(); // A
     System.out.println();
     System.out.println("erwartet: BAA");
     new B().methode(); // BAA
     System.out.println();
     System.out.println("erwartet: CBABAA");
     new C().methode(); } } // CBABAA, rekursiv

In diesem Beispielprogramm wird A.attribut von B.attribut, dieses wiederum von C.attribut verdeckt. Aus B kann man (wie üblich) auf A.attribut mit super.attribut zugreifen. Von C aus ist der Zugriff nur über den konvertierten this möglich: ((A)this).attribut.

Unglücklicherweise funktioniert dies für Methoden nicht: ((A)this).methode(); im obigen try-Block ruft nicht A.methode() sondern C.methode() auf und löst damit eine endlose Rekursion aus. Der Grund ist folgendes.

Die Konvertierung wird vom Compiler durchgeführt: Sie ermöglicht den Zugriff von einer Referenz (hier: this) vom "falschen" Typ auf ein Attribut oder auf eine Methode. Ob dieser Zugriff korrekt ist (ob ein Objekt vom Zieltyp referenziert wird), wird vom Interpreter geprüft und ggf. wird ClassCastException ausgeführt. Die Auswahl einer auszurufenden Methode erfolgt jedoch nicht vom Compiler sondern immer vom Interpreter zur Laufzeit in Abhängigkeit vom Typ des referenzierten Objekts (dies heißt "Polymorphie" und wird durch die "späte Bindung" implementiert). Die Konvertierung verändert nur den Typ der Referenz (für den Compiler), nicht aber des referenzierten Objekts (für den Interpreter). Deswegen wird selbst bei einem konvertierten this immer die Methode aus der aktuellen Klasse (hier C) und nicht aus der Klasse des Zieltyps (hier A) aufgerufen. Eine Methode aus der Ober-Oberklasse kann also nur aufgerufen werden, wenn man ein Objekt der Ober-Oberklasse besitzt:
     new A().methode();
super ist also eine Referenz auf den Oberklassen-Teil des aktuellen Objekts. Die Java-Syntax erlaubt jedoch die Konvertierung von super (im Gegensatz zu this) nicht:
     ((A)super).methode(); // Syntaxfehler


Version: 25. Januar 2012

© Prof. Solymosi, 2011, Beuth-Hochschule für Technik Berlin, Fakultät Informatik und Medien

solymosibht-berlin.de