Home > Informatik > Begriffe und Konzepte > Vererbung

Vererbung

Definition

Vererbung oder Generalisierung

Die Vererbung oder Generalisierung ist ein zentrales Konzept der objektorientierten Programmierung. Eine Subklasse erbt alle Attribute und Methoden der Superklasse.

Bei der Vererbung muss man in Java unterscheiden zwischen

  • Vererbung der Spezifikation
  • Vererbung der Implementierung
Vererbung der Spezifikation

Hier werden nur die Instanzvariablen und die Schnittstellen der Methoden an die Unterklassen weitergereicht. Die Implementierung der Methoden wird jedoch nicht vererbt.

In Java wird diese Art der Vererbung durch Schnittstellenklassen (Interfaces) ermöglicht.

Vererbung der Implementierung

Dies ist der Normalfall der Vererbung, zumindest in Java. Alle Unterklassen können nicht nur auf die Schnittstellen der Oberklasse zurückgreifen, sondern auch auf die Implementierung der Methoden.

Beispiel aus einem Adventure-Game

Wir definieren zunächst eine Klasse Person:

public class Person
{
   protected String name;
   protected int lebenspunkte, angriffswert, verteidigungswert;
   
   public Person(String name)
   {
      this.name = name; 
      lebenspunkte      = 50;     // default-Werte
      angriffswert      = 50;
      verteidigungswert = 50;
   }
   
   public void setLebenspunkte(int leben)
	{  
      lebenspunkte = leben; 
   }
   
   public void setAngriffswert(int angriff)
	{
      angriffswert = angriff; 
   }
   
   public void setVerteidigungswert(int verteidigung)
	{
      verteidigungswert = verteidigung; 
   }
      
}

Wir werden gleich sehen, warum wir die vier Instanzvariablen nicht als private deklarieren dürfen.

Als Nächstes definieren wir eine Klasse Held, die alle Eigenschaften und Verhaltensweisen von Person erbt:

public class Held extends Person
{
    int gold;
    
    public Held(String name)
    {
       super(name);
       gold = 10;
    }
    
    public void trinkeHeiltrank()
    {
       lebenspunkte = 100;
       angriffswert = 100;
       verteidigungswert = 100;
    }
    
    public void findeGold(int gold)
    {
       this.gold += gold;
    }
}

Durch das Schlüsselwort extends legen wir die Klasse Held als Subklasse (oder Unterklasse) von Person fest. Person ist demnach die Superklasse (oder Oberklasse) von Held.

Die vier Instanzvariablen name, lebenspunkte, angriffswert und verteidigungswert der Klasse Held müssen nicht erneut deklariert werden, sie werden von der Oberklasse Person übernommen.

Auch die drei Getter-Methoden werden von der Oberklasse übernommen, wir müssen sie in der Unterklasse nicht noch einmal neu definieren (Vererbung der Implementierung).

Die Unterklasse kann aber zusätzliche Instanzvariablen deklarieren. In unserem Beispiel haben die Objekte der Unterklasse Held ein zusätzliches Attribut Gold, das durch die Instanzvariable gold realisiert wird.

Auch kann eine Unterklasse weitere Methoden deklarieren, die nicht in der Oberklasse enthalten sind. Unsere Unterklasse Held beispielsweise hat zwei zusätzliche Methoden: trinkeHeiltrank() und findeGold().

Hätten wir die Instanzvariablen der Oberklasse als private deklariert, würde der Compiler bei der Anweisung lebenspunkte = 100; den Fehler "lebenspunkte hat private-Zugriff in Person" melden. Als private deklarierte Instanzvariablen sind nämlich nur innerhalb der eigenen Klasse sichtbar, nicht in deren Unterklassen. Mit dem Zugriffsmodifizierer protected jedoch machen wir die vier Instanzvariablen für alle Unterklassen von Person sichtbar, so dass die Methoden dieser Unterklasse sowohl schreibend wie auch lesend darauf zugreifen können.

Zum Schluss deklarieren wir noch eine Klasse Monster, ebenfalls als Unterklasse von Person:

public class Monster extends Person
{
    private boolean sichtbar;
    
    public Monster(String name)
    {
       super(name);
       sichtbar = false;
    }
    
    public void verstecken()
    {
       sichtbar = false;
    }
    
    public void erscheinen()
    {
       sichtbar = true;
    }
}   

Auch die Objekte der Unterklasse Monster erben alle Instanzvariablen und Methoden der Oberklasse Person - sofern sie nicht als private deklariert wurden.

Die Klasse Monster hat eine zusätzliche Instanzvariable sichtbar und zwei zusätzliche Methoden verstecken() und erscheinen(), die diese Instanzvariable manipulieren.

Zum Konstruktor der beiden Subklassen

Die Konstruktoren der Subklassen Held und Monster erhalten den Namen des zukünftigen Helden bzw. Monsters als String-Parameter übermittelt. Durch den Aufruf von super(name); wird nun diese Information an die Superklasse weitergereicht, so dass dort die Instanzvariable name auf den entsprechenden Wert gesetzt werden kann.

Vorsicht Fehler!

Wenn man in der Klasse Held Folgendes schreibt:

public class Held extends Person
{
    int gold;
    
    public Held(String name)
    {
       this.name = name;
       gold = 10;
    }
    ...

erzeugt der Compiler beim Übersetzungsversuch eine Fehlermeldung:

Die Fehlermeldung

Wenn die Anweisung super(name); fehlt, dann ruft der Konstruktor der Klasse Held automatisch den Standard-Konstruktor der Oberklasse auf, und dieser hat keinen Parameter. Der Konstruktor der Oberklasse, den wir implementiert haben, erwartet jedoch einen String-Parameter. Daher müssen wir ihn mit super(name) aufrufen. Die Zeile 8 mit der Anweisung this.name ist dann überflüssig.

Das Klassendiagramm unseres Beispielprojekts:

Das Klassendiagramm

Quellen:

  1. Kecher et al.: UML 2.5 - Das umfassende Handbuch, Rheinwerk Computing 2021