04.06 Polymorphie

Wie man Eigenschaften einer Klasse an eine Neue vererben kann, haben Sie gerade im vorigen Kapitel 04.05 Vererbung erfahren. Es kann aber passieren, dass das Verhalten einer Methode in der Elternklasse nicht unbedingt dem gewünschten Verhalten in der Kindklasse entspricht. Aber auch dafür gibt es ein Konzept in der objektorientierten Programmierung.

Polymorphie

In Kapitel 04.05 Vererbung haben Sie gelernt, wie Objekte einer Kindklasse Methoden der Elternklasse verwenden können, ohne das diese noch einmal explizit in der Kindklasse implementiert wurden. Dies war der Fall bei der Verwendung der Methode getName, welche in der Klasse Person selbst implementiert wurde, aber dennoch von den Objekten der Kinderklassen Manager und Programmierer verwendet werden konnte. Häufig ist es aber erwünscht, dass die Kinderklasse, als speziellere Klasse der Elternklasse, auch speziellere Dinge innerhalb einer solchen Methode durchführt. Zu diesem Zweck gibt es das Konzept der Polymorphie in der objektorientierten Programmierung.

Polymorphie kommt aus dem griechischen und bedeutet Vielgestaltigkeit. Dies soll darauf hinweisen, dass Methoden innerhalb einer Vererbungslinie, trotz gleicher Methodensignatur, ein unterschiedliches Verhalten aufweisen.

Um dies nun einmal zu verdeutlichen, erweitern wir unser Beispiel aus Kapitel 04.05 Vererbung. Die Klasse Person bekommt eine neue Methode sageEtwas.

public class Person {
	
  private String name = "";
	
  public Person(String aName) {
    this.name = aName;
  }
	
  public String getName() {
    return this.name;
  }
	
  public void sageEtwas() {
    System.out.println("Mein Name ist " + this.getName());
  }
}

In der Methode sageEtwas wird einfach ein kleiner Text mit dem Namen der Person ausgegeben. Nun wäre es ja so, dass Objekte der beiden Kindklassen Manager und Programmierer, diese Methode erben. Ruft ein solches Objekt dann diese Methode auf, wird ebenfalls der kleine Text mit dem Namen ausgegeben. Wir möchten aber nun, dass für Manager und Programmierer andere Texte erscheinen, wenn wir die Methode sageEtwas für Objekte dieser Klassen aufrufen. Dazu müssen wir die geerbte Methode innerhalb der Kindklasse überschreiben, um ihr ein anderes Verhalten zu verleihen.

public class Manager extends Person {
	
  private int gehalt;
	
  public Manager(String aName, int aGehalt) {
		
    super(aName);
    this.gehalt = aGehalt;
  }

  public int getGehalt() {
    return this.gehalt;
  }
	
  public void sageEtwas() {
		
    System.out.println("Mein Name ist " + this.getName());
    System.out.println("Ich bin Manager mit einem stolzen Gehalt von " + this.getGehalt());
  }
}

Wir implementieren einfach die bereits in der Klasse Person geschriebene Methode sageEtwas noch einmal innerhalb der Klasse Manager. Dabei ist darauf zu achten, dass auch die Methodensignatur übereinstimmt. Dadurch wird signalisiert, dass diese Methode dieselbe Methode der Elternklasse überschreibt. Innerhalb von sageName verwenden wir durch this.getName() die geerbte Methode getName der Elternklasse Person. Diese haben wir nicht in Manager überschrieben. Dadurch wird diese eins zu eins von der Klasse Person verwendet. Zusätzlich lassen wir den Manager noch mit seinem Gehalt prahlen.
Gleiches gilt auch wieder für unsere Klasse Programmierer.

public class Programmierer extends Person {

  private String lieblingsSprache = "";

  public Programmierer(String aName, String aSprache) {

    super(aName);
    this.lieblingsSprache = aSprache;
  }

  public String getLieblingsSprache() {
    return this.lieblingsSprache;
  }

  public void sageEtwas() {

    System.out.println("Mein Name ist " + this.getName());
    System.out.println("Ich bin Programmier und arbeite am liebsten mit " + this.getLieblingsSprache());
  }
}

Hier wird ebenfalls eine speziellere Methode sageEtwas implementiert. Dies testen wir nun wieder.

public static void main(String[] args) {
		
  Person personen[] = new Person[3];
	
  personen[0] = new Person("Horst");
  personen[1] = new Programmierer("Sebastian", "Java");
  personen[2] = new Manager("Wendelin", 50000000);
		
  for (int i = 0; i < personen.length; i++) {
    personen[i].sageEtwas();
  }
}

In der main Methode legen wir ein Array personen mit verschiedenen Objekten der Klasse Person bzw. Kinderklassen davon an. Danach soll in einer Schleife jede dieser Personen mit personen[i].sageEtwas(); etwas sagen. Wenn Sie aufmerksam mitlesen, werden Sie nun folgendes feststellen: Alle Elemente des Arrays personen sind doch Objekte der Klasse Person. Der Aufruf personen[i].sageEtwas() würde also demnach bedeuten, es wird immer Person.sageEtwas() verwendet. Dies ist aber nicht der Fall. Abhängig von dem Typ des aktuellen Objektes, wird sich zur Laufzeit die entsprechende Methode ausgesucht. In unserem Fall implementieren alle Klasse die Methode sageEtwas. So wird im Endeffekt, passend zum aktuellen Objekt, auch die dazugehörige Methode verwendet. Als Ausgabe erhalten wir.

Mein Name ist Horst
Mein Name ist Sebastian
Ich bin Programmier und arbeite am liebsten mit Java
Mein Name ist Wendelin
Ich bin Manager mit einem stolzen Gehalt von 50000000

In den überschriebenen Methoden verwenden wir immer die Zeile

System.out.println("Mein Name ist " + this.getName());

Dies ist ja eigentlich schon Bestandteil der Methode sageEtwas in der Elternklasse Person. Um uns nicht immer zu wiederholen, können wir auch Methoden der Elternklasse in den Kindklassen verwenden. Dies geht mit dem Schlüsselwort super. Das funktioniert analog zur Verwendung der Konstruktoren der Elternklasse wie in Kapitel 04.05 Vererbung beschrieben.
Schreiben wir nun einmal die Methode sageEtwas in den Klassen Manager und Programmierer um.

public void sageEtwas() {
		
  super.sageEtwas();
  System.out.println("Ich bin Manager mit einem stolzen Gehalt von " + this.getGehalt());
}

bzw.

public void sageEtwas() {

  super.sageEtwas();
  System.out.println("Ich bin Programmierer und arbeite am liebsten mit " + this.getLieblingsSprache());
}

Wir verwenden nun als erstes die Methode sageEtwas der Elternklasse und anschließend ergänzen wir die Methode sageEtwas der Kindklasse um den speziellen Teil. Dies hat vor allem den Vorteil, dass man nun nicht mehr an drei Stellen im Quellcode Änderungen vornehmen muss, wenn sich etwas an dem generellen Teil der Methode sageEtwas der Elternklasse ändern sollte. Führen wir nun eine Änderung der Methode sageEtwas in der Klasse Person durch, so hat dies auch Auswirkungen auf die Kinderklassen, welche diese Methode verwenden bzw. erben.

2 Replies to “04.06 Polymorphie”

  1. Cyrill Brunner

    Was genau ist der Sinn hinter dem @Override? Ich verstehe nämlich nicht, was es so wichtig macht, es an manchen Stellen zu setzen und an manchen nicht

  2. Stefan Kiesel

    Hallo Cyrill,

    @Override ist eine Annotation, welche es seit Java 5 gibt. Sie zeigt dem Compiler an, dass hier eine Methode einer Vaterklasse überschrieben werden soll. Zum einen erleichtert es die Lesbarkeit vom Code, weil auf den ersten Blick ersichtlich ist, dass eine Methode eine andere überschreibt. Zum anderen kann es sein, dass Sie nur denken eine Methode zu überschreiben, in Wirklichkeit tuen sie das aber nicht (bspw. Buchstabendreher im Methodennamen, Parameter stimmen nicht mit der zu überschreibenden Methode überein, …). Ohne @Override fällt das oftmals nicht sofort auf. Verwenden Sie aber @Override bei allen Methoden die Sie überschreiben möchten, meckert der Compiler, falls die Methodensignatur nicht passt, also wenn Sie eine nicht (korrekt) überschriebene Methode mit @Override versehen.

    Grüße
    Stefan

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.