Polymorphie Warum funktionieren polymorphe Referenzvariablen bei überschriebenen Methoden und bei nicht überschriebenen nicht?

BigMemo007

Aktives Mitglied
Hallo Forum,

entschuldigt bitte meine eventuell nicht gut formulierten Sätze. Ich bin neu bei Java (zwei Wochen) und frischling in diesem Forum.

Ich habe folgende Frage: Wie kann es sein, dass mit polymorphen Referenzvariablen der Aufruf von überladenen Methoden funktioniert und der nicht überladenen eben nicht?
Folgendes Beispiel:


Code:
public class Tier{

   public void fressen(){
       System.out.println("Ich FRESSE wie ein TIER.");
  }
}


public class Hund extends Tier{

   public void fressen(){    //überschriebene Methode
        System.out.println("Ich FRESSE wie ein HUND.");
   }
   public void bellen(){  //neue Methode; wurde nicht geerbt
     System.out.println("Ich BELLE wie ein HUND.");
    }
}


public class Main{

   public static void main(String [] args){
       Tier h = new Hund();  //Referenzvariable aus Tier und Objekt ist ein Hund
        h.fressen();   //frisst wie ein Hund
        h.bellen();    //bellen wird nicht gefunden für h, weil eben Datentyp Tier
   }

}

Warum funktioniert das Fressen wie ein Hund mit h.fressen(), aber bellen wird nicht gefunden? Wie kann die JVM das Objekt Hund bei der Methode fressen() als Hund identifizieren und beim bellen() nicht? Ich hatte mir vorgestellt, dass die JVM Objekte anhand ihrer Referenzvariablen erkennt und nicht anhand ihrer Instanzvariablen, weil diese eben nur Werte sind, die der Programmierer vergibt. Woher soll die JVM wissen, wenn der String art = "Dackel"; ist, dass es sich um einen Hund handelt?
 
Zuletzt bearbeitet:
Wir müssen hier zwischen zwei Arten von Typen unterscheiden:
1. Compilezeit-Typen (statisch)
2. Laufzeit-Typen (dynamisch)
Was der Compiler sieht, ist der Compilezeit-Typ. Das ist der bei der Deklaration einer Variablen, Methodenparameter oder Methodenrückgabetyp angegebene Typ (zusätzlich kommen noch andere Stellen hinzu, wie etwa Typparameter bei generischen Typen). Der Compilezeit-Typ wird auch statischer Typ genannt, weil er eben statisch (unverändert) ist und sich nicht erst dynamisch zur Laufzeit durch den Programmfluss ergibt.

Ein Compilezeit-Typ bzw. statischer Typ ist also der Typ z.B. einer Variablen, oder eines Methodenparameters. Allgemeiner kann man sagen, dass einem Ausdruck in Java ein statischer Typ zur Compilezeit zugeordnet wird. Auf Basis dieses statischen Typs werden dann weitere Typchecks gemacht.

In deinem Fall ist der Compilezeit- bzw. statische Typ der Variablen `t` gleich `Tier`. Und alles, was den Compiler interessiert ist: Deklariert dieser Typ die Methode `bellen()`. Nein. Also -> Fehler!

Im Gegensatz zu dem statischen Typ existiert der dynamische Typ oder auch Laufzeittyp. Dieser Typ wird nicht an syntaktischen Elementen der Sprache annotiert, wie z.B. bei dem statischen Typ an Ausdrücken, sondern es ist der Typ von Werten, die durch Ausdrücke generiert werden. Zum Beispiel hat direkt nach der Deklaration deiner `Tier h = new Hund()` Variable die Variable `h` den statischen Typ `Tier`. Wenn wir nun nur die Variable `h` betrachten und diese in einem Ausdruck dynamisch zur Laufzeit auswerten würden, dann hätte das Objekt, welches durch diesen Ausdruck zurückgeliefert wird, den dynamischen Laufzeittyp `Hund`, weil ja zum Zeitpunkt der Auswertung dieser Variable eine Instanz der Klasse `Hund` dort gespeichert ist.

Also:
- Statischer Typ gleich "Typ eines Ausdrucks zur Compilezeit"
- Dynamischer Typ gleich "Typ des durch den Ausdruck zur Laufzeit generierten Wertes/Objektes"

Dynamic Dispatch bzw. zu Deutsch "spätes Binden" passiert zur Laufzeit (also dynamisch) auf Basis des dynamischen Typs des Wertes/Objektes.
 
Der Compiler prüft zum Zeitpunkt der Compilierung anhand der Deklaration (in diesem Fall Tier), ob garantiert ist, dass eine entsprechende Methode existiert. Für Tier gibt es eine Methode fressen, aber keine Methode bellen.

Zum Ausführungszeitpunkt prüft die Laufzeitumgebung anhand des konkreten Typs (in diesem Fall Hund), in welcher Weise gefressen und gebellt werden soll. Da kommt dann die Polymorphie zum Tragen.

Zum Zeitpunkt der Compilierung ist zwar in deinem Beispiel, aber eben nicht grundsätzlich klar, ob es sich bei dem Tier überhaupt um einen Hund handelt. Über die Art der Zuweisung könnte ja auch z.B. per Zufallsgenerator erst zur Laufzeit entschieden werden, so dass die Variable h etwa eine Katze referenziert, die überhaupt nicht bellen kann.
 

Zurück
Oben