Bene: Frage zu Polymorphie in Java

Hallo,

eine etwas kompliziert zu erklärende Frage, die uns bei der Klausurvorbereitung gekommen ist.
Gegeben sind folgende Klassen:

  
public class A  
{  
 public int p;  
 public B q;  
 public A(B q)  
 {  
  this.q = q;  
  p = 5;  
 }  
}  
  
public class B  
{  
 public int r;  
 public String s;  
 public void f()  
 {  
  System.out.println(s);  
 }  
}  
  
public class C extends B  
{  
 public double t;  
 public void f()  
 {  
  System.out.println(s+ ", " + t);  
 }  
}  
  
public class Tester {  
  
 public static void main(String[] args)  
 {  
  C v = new C();  
  v.r = 4;  
  v.s = "Maus";  
  v.t = 3.4;  
  v.f();  
  B w = v;  
  w.f();//???  
  C x = (C) v;  
  x.f();  
  w = new B();  
  w.r = 5;  
  w.s = "Hase";  
  w.f();  
  w = null;  
  x = null;  
  A y = new A(v);  
  v = new C();  
  v.r = 5;  
  v.s = "Schlange";  
  v.t = 7.4;  
 }  
}  
  

Die Frage betrifft den Aufruf der Methode f() auf dem Objekt w. (Zeile markiert mit drei Fragezeichen im Kommentar). Diese Methode erzeugt ebenfalls die Ausgabe "Maus, 3.4". Wegen der Polymorphie wird die Methode f() der Klasse C aufgerufen, soweit so klar.
Aber: nach unseren Wissen kann man über ein Referenz auf eine Vaterklasse nicht Attribute zugreifen, die in der Sohnklasse hinzugefügt wurden (hier double t). Dies passiert aber in diesem Fall, wenn auch indirekt über die überschriebene Methode f.
Wird hier intern ein expliziter Downcast durchgeführt, oder was ist der Grund das dies funktioniert?

Ich hoffe es verständlich erklärt zu haben.
Danke für eure Hilfe
Bene

  1. Hallo,

    Aber: nach unseren Wissen kann man über ein Referenz auf eine Vaterklasse nicht Attribute zugreifen, die in der Sohnklasse hinzugefügt wurden (hier double t). Dies passiert aber in diesem Fall, wenn auch indirekt über die überschriebene Methode f.

    Ja. Die Methode gehört zu C und kennt dann natürlich alles, was zur
    Klasse gehört.

    Wird hier intern ein expliziter Downcast durchgeführt, oder was ist der Grund das dies funktioniert?

    Der Compiler denkt nicht in Casts.

    Ein Cast ist eigentlich nur ein Hinweis an den Compiler, daß es sich
    bei einem konkreten Objekt um eine Instanz einer anderen (üblicherweise
    weiter unten in der Hierarchie befindlichen) Klasse handelt. Den
    Hinweis gibt der Mensch, der das zum Zeitpunkt der Entwicklung weiß.
    Wenn der Mensch eine falsche Angabe macht, wird während das während
    der Ausführung(!) bemerkt und eine ClassCastException geworden.

    Polymorphismus hat überhaupt nichts mit Casts zu tun. Beim konkreten
    Aufruf der Methode "f" wird die passende Methode (zur Laufzeit!)
    ermittelt und aufgerufen. Die Laufzeitumgebung weiß natürlich jederzeit
    welches konkrete Objekt sich hinter einer Referenz versteckt.
    Entsprechend wird das passende "f" im passenden Objekt aufgerufen, so
    daß die Methode "f" vollen Zugriff auf das Objekt hat. Es handelt sich
    ja schließlich um eine Methode des Objekts.

    Man muß hier wirklich ein bißchen zwischen Compile- und Ausführzeit
    unterscheiden. Casts dienen dazu, dem Compiler mitzuteilen, daß es
    sich -- grob gesprochen -- bei einem Objekt eigentlich um ein
    höherwertiges Objekt handelt, als er aktuell denkt. Das ist nötig, um
    Zugriff auf die Methoden oder Attribute zu bekommen, die das höher-
    wertige Objekt besitzt. Der Compiler kann das selbst nicht wissen,
    weil der Inhalt der Referenz (je nach Typ) beliebig während der
    Laufzeit gesetzt werden kann. Der Mensch hingegen weiß (optimalerweise)
    wobei es sich wirklich handelt und fügt daher den Cast ein.

    Zum Beispiel kann ich jedes konkrete Objekt (z.B. String) immer als
    Referenz auf ein Object-Objekt verwenden. Dann steht mir nur das
    zur Verfügung, was Object anbietet. Alles andere ist nicht verfügbar,
    weil der Compiler ja nicht wissen kann, daß in der Object-Referenz
    in Wirklichkeit ein String steht. (Während der Laufzeit weiß die
    Laufzeitumgebung das hingegen sehr wohl.)
    Um jetzt trotz Object-Referenz auf Inhalt des String-Objekts zugreifen
    zu können, muß das Object in das konkretere "String" gecastet werden.
    Dann weiß der Compiler(!), daß es sich in Wirklichkeit um einen String
    handelt und erlaubt weitere Operationen.

    Zur Laufzeit, also wenn es zum Polymorphismus kommt und die passende
    Methode in der Hierarchie "gefunden" werden muß, ist der Laufzeit-
    umgebung natürlich klar, welches tatsächlice Objekt sich hinter einer
    Referenz wirklich verbirgt, also kann die Methode aufgerufen werden.
    Diese muß die Attribute ohnehin kennen, weil ja sonst auch keine
    Kompilierung möglich gewesen wäre.

    Der Compile-Vorgang funktioniert hier auch ohne dieses wissen, weil
    die Methoden eine definierte Parameterliste haben und den gleichen
    Rückgabewert besitzen, auch wenn sie weiter unten in der Hierarchie
    nochmal überschrieben werden. Es ist daher immer möglich, den Quellcode
    zu kompilieren, weil die Methode immer existiert.

    Gruß
    Slyh

    1. Hallo,

      Vielen Dank für die ausführliche und hilfreiche Antwort!
      Jetzt ist es klar!

      Viele Grüße
      Bene