Liskov Substitution

Subtypen müssen in ihrem Verhalten den Basistypen entsprechen.

Betrachten wir das ganze anhand eines Beispiels:

interface Bike {
    void turnOnEngine();

    void accelerate();
}

Wir haben das Interface Bike, welches zwei Methoden definiert: turnOnEngine und accelerate. Zwei Klassen implementieren dieses Interace Motorbike und Bicycle.

class Motorbike implements Bike {

    boolean isEngineOn;
    int speed;

    @Override
    public void turnOnEngine() {
        isEngineOn = true;
    }

    @Override
    public void accelerate() {
        speed += 5;
    }
}

Motorbike hat für beide Methoden eine korrekte Implementierung. turnOnEngine setzt den Wert von isEngineOn auf true und accelerate erhöht die Geschwindigkeit um 5km/h.

class Bicycle implements Bike {

    boolean isEngineOn;
    int speed;

    @Override
    public void turnOnEngine() {
        throw new AssertionError("There is no engine!");
    }

    @Override
    public void accelerate() {
        speed += 5;
    }
}

Im Gegensatz dazu hat Bicycle eine fehlerhafte Implementierung von turnOnEngine. Da ein Fahrrad keinen Motor hat, sollte die Methode turnOnEngine nicht aufgerufen werden. In diesem Fall wird eine AssertionError geworfen. Das ist ein Verstoß gegen das Liskov Substitution Prinzip. Sollte eine Klasse ein Interface implementieren, sollte alle Instanzen des Interfaces das gleiche Verhalten haben. In diesem Fall sollte turnOnEngine für alle Klassen, die das Interface Bike implementieren, den Motor anschalten. Die Implmentation von Bicycle ist also fehlerhaft.