Polymorphismus und Konstruktoren in Java

Polymorphe Methodenaufrufe können in Konstruktoren zu schwerwiegenden Programmfehlern führen, die sehr schwierig zu lokalisieren sind.

Aufgrund der Initialisierungsreihenfolge von Konstruktoren besteht die Gefahr, dass Variablen der abgeleiteten Klasse noch nicht initialisiert wurden, die polymorph aufgerufenen überlagerten Methoden diese Variablen jedoch schon verwenden.

Daher sollten Aufrufe von Methoden, welche möglicherweise in Subklassen überlagert werden, nicht in Konstruktoren erfolgen.

Die Initialisierungsreihenfolge von Konstruktoren sollte jedem Java-Entwickler bestens bekannt sein. Wir behandeln sie in unserem Beitrag: Konstruktoren und Destruktoren in Java ausführlich, an dieser Stelle soll uns aber die folgende kurze Beschreibung genügen.

plhq_teaser_hbox_gelb_fotolia_RA Studio_46292813

Unser großes
Android Online-Kurs
Gesamtpaket



Weitere Infos

In Java werden bei jeder Instanzierung die folgenden beiden Schritte ausgeführt:

  1. Die Konstruktoren der Vaterklassen werden aufgerufen (von der eigenen Klasse bis hoch zur Urklasse object). Dabei werden die Variablen der Vaterklassen initialisiert und anschließend die Anweisungen in den Vaterklassen-Konstruktoren ausgeführt.
  2. Danach werden die Variablen der eigenen Klasse initialisiert und die Anweisungen des eigenen Konstruktors ausgeführt.

Wird nun in einem Konstruktor einer Vaterklasse eine Methode aufgerufen, die in einer abgeleiteten Klasse überlagert wurde, sind zu diesem Zeitpunkt die Variablen der Klasse noch nicht initialisiert. Dies würde erst später in Schritt 2 erfolgen, wenn der Konstruktor der abgeleiteten Klasse ausgeführt wird.

Die folgende Beispielanwendung verdeulticht welche Gefahr polymorph aufgerufene Methoden in Konstruktoren darstellen können:

Beispielanwendung für Polymorphismus und Konstruktoren in Java

/*
* Beispielanwendung für Polymorphismus und Konstruktoren in Java
*/

class EinfacheBotschaft
{
   String meldung1;

   public EinfacheBotschaft(String text)
   {
      meldung1 = text;

      gebeBotschaftAus();
   }

   public void gebeBotschaftAus()
   {
      System.out.println("Erste Meldung: " + meldung1);
   }
}

class ZweifacheBotschaft extends EinfacheBotschaft
{
   String meldung2;

   public ZweifacheBotschaft(String text1, String text2)
   {
      super(text1);
      meldung2 = text2;

      gebeBotschaftAus();
   }

   public void gebeBotschaftAus()
   {
      System.out.println("Erste Meldung: " + meldung1 + " Zweite Meldung: " + meldung2);
   }
}

public class InitialisierungsTest
{
  public static void main (String[] args)
  {
     System.out.println("\nZB-Objekt in Variable vom Typ EB:");
     EinfacheBotschaft obj1 = new ZweifacheBotschaft("Zweifache", "Botschaft");

     System.out.println("\nZB-Objekt in Variable vom Typ ZB:");
     ZweifacheBotschaft obj2 = new ZweifacheBotschaft("Zweifache", "Botschaft");

     System.out.println("\nEB-Objekt in Variable vom Typ EB:");
     EinfacheBotschaft obj3 = new EinfacheBotschaft("Einfache");
  }
}

In Zeile 45 wird ein neues Objekt vom Typ ZweifacheBotschaft erzeugt und die Referenz zum Objekt in der Referenzvariable obj1 vom Typ EinfacheBotschaft abgespeichert. Bei der Instanzierung des Objekts wird zunächst der Konstruktor der Superklasse (Vaterklasse) aufgerufen und dieser initialisiert seine Instanzvariable meldung1.

Danach wird die Methode gebeBotschaftAus() polymorph aufgerufen. Da das zu instanzierende Objekt vom Typ ZweifacheBotschaft ist, wird die Methodenimplementierung aus der Klasse ZweifacheBotschaft ausgeführt. Diese Methodenversion verwendet aber neben der Variable meldung1 zusätzlich die Variable meldung2, welche zu diesem Zeitpunkt noch nicht initialisiert ist.

Daher wird als Inhalt der Variable meldung2 null ausgegeben. Die Initialisierung von meldung2 erfolgt erst nachdem der Konstruktor von EinfacheBotschaft komplett abgearbeitet ist.

Nachdem der Konstruktor von EinfacheBotschaft komplett abgearbeitet worden ist, wird zu dem Konstruktor der eigenen Klasse ZweifacheBotschaft zurückgesprungen und die Variable meldung2 initialisiert. Danach wird die Methode gebeBotschaftAus() erneut aufgerufen, diesmal werden beide Meldungen ausgegeben und das erwartete Ergebnis tritt ein.

In Zeile 48 wird ein neues Objekt vom Typ ZweifacheBotschaft erzeugt und die Referenz zum Objekt in der Referenzvariable obj2 vom Typ ZweifacheBotschaft abgespeichert. Auch in diesem Fall tritt der Initialisierungsfehler wegen dem polymorphen Methodenaufruf in dem Konstruktor der Vaterklasse auf.

In Zeile 51 wird ein neues Objekt vom Typ EinfacheBotschaft erzeugt und die Referenz zum Objekt in der Referenzvariable obj3 vom Typ EinfacheBotschaft abgespeichert. Diesmal gibt die Anwendung das erwartete Ergebnis aus.

Die Methodenimplementierung aus der Klasse ZweifacheBotschaft ist hier nicht bekannt, da die Referenzvariable obj3 vom Typ EinfacheBotschaft ist und daher nur Methodenimplementierungen der Klasse EinfacheBotschaft aufgerufen werden können und keine Versionen der Subklassen.

Starten wir nun die Beispielanwendung. Die dabei erzeugte Textausgabe ist in der unten abgebildeten Kommandozeilenausgabe dargestellt:

Java Polymorphismus Konstruktoren

Java Polymorphismus und Konstruktoren – Ausgabe der Beispielanwendung

Zurück zur Java Kurs Hauptseite.


Comments 2

  1. Pingback: Vererbung und Polymorphismus in Java

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.