Home > Informatik > Stufe Q1

Vererbung

Allgemeines - Klassendiagramme - Mehrfachvererbung

Problemstellung

In dem Dungeon-Spiel, das ich häufig mit meinen Schülern der Stufen EF oder Q1 programmiere, kommen regelmäßig zwei wichtige Java-Klassen vor: Held und Monster. Im Grunde unterscheiden sich die beiden Klassen nur wenig. Beide Klassen verfügen über Attribute wie angriff, verteidigung, leben und so weiter. In den fortgeschrittenen Versionen des Spiels können sich die Objekte beider Klassen auch bewegen, Gegenstände aufnehmen, kämpfen und so weiter.

Nun könnte man beide Klassen völlig unabhängig voneinander programmieren, vielleicht sogar mit Arbeitsteilung: Eine Schülergruppe implementiert die Klasse Held, eine andere Gruppe die Klasse Monster. Oft funktioniert diese arbeitsteilige Vorgehensweise sogar.

Hier eine Kurzfassung einer solchen Klasse Held:

public class Held
{
    int x,y;
    double stufe, leben, angriff, verteidigung;
    Gegenstand[] dinge;
    int anzahlDinge;
    
    public Held(int xPos, int yPos)
    {
        x = xPos;
        y = yPos;
        stufe = 1;
        leben = 100;
        angriff = 20;
        verteidigung = 20;
        dinge = new Gegenstand[12];
        anzahlDinge = 0;
    }
    
    public void goWest()
    {
       if (x > 0) x--;
    }
    
    public int getLeben()
    {
       return (int) Math.round(leben);
    }
    
    public void aufnehmen(Gegenstand neu)
    {
       if (anzahlDinge < 12)
          dinge[anzahlDinge++] = neu;
    }
}

Und hier eine Kurzfassung der Klasse Monster:

public class Monster
{
    int x,y;
    double stufe, leben, angriff, verteidigung;
    
    public Monster(int xPos, int yPos)
    {
        x = xPos;
        y = yPos;
        stufe = 1;
        leben = 100;
        angriff = 15;
        verteidigung = 15;
    }
    
    public void goWest()
    {
       if (x > 0) x--;
    }
    
    public int getLeben()
    {
       return (int) Math.round(leben);
    }
}

Wie man leicht sieht, unterscheiden sich diese beiden Klassen kaum. Wenn sich aber grundlegende Dinge im Gesamtprojekt ändern, müssen die Klassen entsprechend umgeschrieben werden - und zwar beide Klassen. Dabei passiert es oft, dass man die eine Klasse verändert, dies bei der anderen Klasse aber vergisst oder irgendwie anders macht. Und schon hat man Probleme.

Die Lösung heißt Vererbung

Wir schreiben zunächst eine neue Klasse Lebewesen, weil das die gemeinsame Eigenschaft von Helden und Monstern ist, beide sind Lebewesen. In diese Klasse packen wir alle Attribute und Methoden hinein, die sowohl Monster wie auch Helden besitzen sollen. Dann leiten wir die Klassen Held und Monster von dieser Klasse Lebewesen ab. Das Grundprinzip heißt hier Vererbung.

public class Lebewesen
{
    int x,y;
    double stufe, leben, angriff, verteidigung;
    
    public Lebewesen(int xPos, int yPos)
    {
        x = xPos;
        y = yPos;
        stufe = 1;
        leben = 100;
        angriff = 10;
        verteidigung = 10;
    }
    
    public void goWest()
    {
       if (x > 0) x--;
    }
    
    public void goNorth()
    {
       if (y > 0) y--;
    }
    
    public int getLeben()
    {
       return (int) Math.round(leben);
    }
    
    public void levelUp()
    {
       if (stufe < 80)
       {
           stufe++;
           angriff *= 1.1;
           verteidigung *= 1.1;
           leben = 100;
       }
    }
    
}

Natürlich ist dieser Quelltext nicht vollständig; viele Methoden wie zum Beispiel goEast oder goSouth fehlen noch. Es geht hier ja nur ums Prinzip.

Betrachten wir nun die Klassen Held und Monster, die sich von Lebewesen ableiten. Zunächst die Klasse Held:

public class Held extends Lebewesen
{
    Gegenstand[] dinge;
    int anzahlDinge;
    
    public Held(int xPos, int yPos)
    {
        super(xPos,yPos);
        angriff = 20;
        verteidigung = 20;
        dinge = new Gegenstand[12];
        anzahlDinge = 0;
    }
    
    public void aufnehmen(Gegenstand neu)
    {
       if (anzahlDinge < 12)
          dinge[anzahlDinge++] = neu;
    }
}

Und dann die Klasse Monster:

public class Monster extends Lebewesen
{
    public Monster(int xPos, int yPos)
    {
        super(xPos,yPos);
        angriff = 25;
        verteidigung = 25;
    }
}

"Vererbung" heißt, dass die Klasse Held alle Attribute und Methoden übernimmt, die in der "Mutterklasse" Lebewesen definiert worden sind. Alle Objekte der Klasse Held besitzen also ein Attribut leben, ein Attribut angriff und so weiter, und alle Objekte der Klasse Held verfügen über eine Methode goWest, goNorth, getLeben und so weiter. Das Gleiche gilt für alle Objekte der Klasse Monster, auch sie besitzen alle Attribute und Methoden, die in der Klasse Lebewesen definiert worden sind.

Die Tochterklassen können einen eigenen Konstruktor besitzen, so wie in den obigen Beispielen. Mit der Anweisung super() kann dann der Konstruktor der Mutterklasse aufgerufen werden; die benötigten Parameter müssen dann mit übergeben werden.

Für Experten

Ganz korrekt ist die Aussage nicht, dass die Tochterklassen alle Attribute und Methoden der Mutterklasse erben. Attribute und Methoden, die als private deklariert worden sind, können nicht vererbt werden.

Betrachten wir mal den Anfang der zu Testzwecken leicht ergänzten Klasse Lebewesen:

public class Lebewesen
{
    int x,y;
    double stufe, leben, angriff, verteidigung;
    
    private int test;
    ...

Wir wollen nun im Konstruktor der Klasse Held dieses private-Attribut ändern:

    public Held(int xPos, int yPos)
    {
        super(xPos,yPos);
        angriff = 20;
        verteidigung = 20;
        dinge = new Gegenstand[12];
        anzahlDinge = 0;
        test = 213;
    }

Beim Kompilieren der Klasse Held zeigt uns BlueJ dann eine Fehlermeldung: "test has private access in Lebewesen".

Wie die obigen Beispiele zeigen, kann eine Tochterklasse neue Attribute und Methoden besitzen. Die Klasse Held verfügt zum Beispiel über einen Objektarray der Klasse Gegenstand, damit der Held Dinge aufsammeln kann, die im Dungeon herumliegen. Die Monster können das nicht, daher brauchen sie auch keinen solchen Objektarray. Die Methode aufnehmen kommt auch nur in der Klasse Held vor, da die Monster keine Gegenstände aufnehmen können.

Damit haben wir schon einmal ein paar grundlegende Dinge über Vererbung gelernt.

Vererbung ist ein wichtiges Grundprinzip der objektorientierten Programmierung. Bei der Vererbung übernehmen die Tochterklassen alle nicht als private deklarierten Attribute und Methoden der Mutterklasse. De facto IST eine Tochterklasse identisch mit der Mutterklasse, daher bezeichnet man die Vererbungsbeziehung auch als IST-Beziehung.

Die Bezeichnung IST-Beziehung ist nicht perfekt, denn wie wir gerade gesehen haben, können die Tochterklassen zusätzliche Attribute und Methoden besitzen, sie sind also sogar MEHR als die Mutterklasse.