CS_LEVEL_3_SOLUTION - OnlyCook/abitur-elite-code GitHub Wiki

Level 3 – Musterlösung: Abstrakte Klassen und Vererbung

Lösung

public abstract class Tier
{
    protected string name;
 
    public Tier(string name)
    {
        this.name = name;
    }
}
 
public class Loewe : Tier
{
    private int laenge;
 
    public Loewe(string name, int laenge) : base(name)
    {
        this.laenge = laenge;
    }
 
    public string Bruellen()
    {
        return "ROAR!!!";
    }
}

Erklärung

Abstrakte Klassen

Eine abstrakte Klasse ist eine Klasse, von der man keine direkten Objekte erstellen kann. Sie dient ausschließlich als Vorlage (Basisklasse) für andere Klassen, die von ihr erben.

public abstract class Tier { ... }

Das bedeutet: new Tier(...) würde einen Compilerfehler erzeugen. Man kann aber sehr wohl new Loewe(...) schreiben, weil Loewe eine konkrete (nicht abstrakte) Klasse ist.

Warum abstrakt? Im echten Zoo gibt es kein Tier, das einfach nur „Tier" ist – es ist immer ein Löwe, ein Elefant, usw. Abstrakte Klassen bilden genau das ab: das Konzept existiert, aber nie ohne eine konkrete Ausprägung.

In Java und C# ist die Syntax für abstrakte Klassen identisch: abstract class.


Der protected-Zugriffsmodifikator

Bisher kennen wir private (nur innerhalb der Klasse) und public (überall). Das Attribut name ist hier protected:

protected string name;
Modifikator Zugriff von der eigenen Klasse Zugriff von Unterklassen Zugriff von außen
private
protected
public

Im UML-Diagramm wird protected mit einem # gekennzeichnet. protected macht Sinn, wenn Unterklassen das Attribut kennen und damit arbeiten sollen, es aber trotzdem nicht frei zugänglich sein soll.


Vererbung mit :

Vererbung bedeutet: Eine Klasse übernimmt alle Attribute und Methoden einer anderen Klasse und kann sie erweitern.

public class Loewe : Tier { ... }

Der Doppelpunkt : ist die C#-Syntax für Vererbung. In Java würde man stattdessen extends schreiben:

Java C#
class Loewe extends Tier class Loewe : Tier

Loewe ist die Unterklasse (auch: abgeleitete Klasse), Tier ist die Oberklasse (auch: Basisklasse). Loewe erbt name von Tier – es muss also nicht nochmal deklariert werden.


Der Basis-Konstruktor mit base

Wenn eine Unterklasse einen Konstruktor hat, muss sie sich auch darum kümmern, dass der Konstruktor der Oberklasse aufgerufen wird. Das geschieht mit : base(...) direkt hinter dem Konstruktor-Kopf:

public Loewe(string name, int laenge) : base(name)
{
    this.laenge = laenge;
}

Hier passiert folgendes der Reihe nach:

  1. Loewe empfängt name und laenge als Parameter.
  2. : base(name) ruft den Konstruktor von Tier auf und übergibt nameTier setzt damit this.name.
  3. Danach wird this.laenge = laenge im Körper von Loewe ausgeführt.

Loewe selbst muss name also nicht nochmal zuweisen – das erledigt die Oberklasse. laenge hingegen ist nur in Loewe bekannt, also wird es hier gesetzt.

In Java heißt das Äquivalent super(name) – die Logik ist identisch, nur die Schlüsselwörter unterscheiden sich.

Java C#
super(name) base(name)

Die Methode Bruellen()

public string Bruellen()
{
    return "ROAR!!!";
}

Das ist eine ganz normale Methode in Loewe. Sie gibt einen string zurück – der konkrete Inhalt ist frei wählbar, solange der Rückgabetyp stimmt. Da Tier diese Methode nicht kennt, gehört sie ausschließlich zur Klasse Loewe.


Das Gesamtbild

        Tier  (abstract)
        ├── protected string name
        └── public Tier(name)
               ↑ erbt von / base(name)
        Loewe
        ├── private int laenge
        ├── public Loewe(name, laenge)
        └── public string Bruellen()

Loewe ist eine Spezialisierung von Tier: Es bringt alles mit, was Tier definiert, und ergänzt darüber hinaus eigene Attribute und Methoden.