Abstrakte Klasse Vs. Schnittstelle in Java: Erklärung mit Beispielen

In Java dienen abstrakte Klassen und Schnittstellen der Abstraktion. Abstraktion im Kontext der objektorientierten Programmierung bedeutet, dass Implementierungsdetails vor dem Endnutzer verborgen werden.

Durch Abstraktion ist es möglich zu erfahren, welche Funktionalitäten vorhanden sind, ohne jedoch zu wissen, wie diese konkret umgesetzt wurden.

Wir betrachten nun beide Konzepte genauer, um deren jeweilige Anwendungsbereiche besser zu verstehen.

Abstrakte Klasse

Eine abstrakte Klasse ist in Java eine Klasse, die nicht als Objekt instanziiert werden kann und die abstrakte Methoden enthalten kann. Eine abstrakte Methode ist eine Methode, für die bei der Deklaration keine Implementierung bereitgestellt wird.

Ein typisches Beispiel für eine abstrakte Klasse ist die Klasse `GraphicObject` aus dem Oracle Tutorial.

Um eine abstrakte Klasse zu erstellen, wird das Schlüsselwort `abstract` dem Schlüsselwort `class` vorangestellt.

abstract class AbstrakteKlasse {
    void ausfuehren() {
        System.out.println("ausgeführt");
    }
}

Eine abstrakte Klasse kann von anderen Klassen erweitert werden, d.h., es können Unterklassen erstellt werden, die auf der abstrakten Klasse aufbauen.

abstract class AbstrakteKlasse {
    void ausfuehren() {
        System.out.println("ausgeführt");
    }
}

class ErweiterteAbstrakteKlasse extends AbstrakteKlasse {
    void neueMethode() {
        System.out.println("neu");
    }

    @Override
    void ausfuehren() {
        System.out.println("überschrieben");
    }
}

Abstrakte Klassen werden genutzt, um gemeinsame Methoden für mehrere Klassen bereitzustellen, die eine gegebene abstrakte Klasse erweitern. Die Möglichkeit, abstrakte Methoden innerhalb abstrakter Klassen zu definieren, macht sie besonders nützlich für Klassen, die zwar ähnliche Methoden haben, deren Implementierung jedoch unterschiedlich sein soll. Betrachten wir dazu ein Beispiel:

Stellen wir uns ein Auto vor, das Funktionen wie Starten, Stoppen und Rückwärtsfahren besitzt. Diese Funktionen sind bei allen Automodellen gleich.

Wie aber verhält es sich mit Automatisierungsfunktionen, wie etwa dem autonomen Fahren? Die Implementierung dieser Funktionen kann für verschiedene Fahrzeugtypen unterschiedlich sein. Wir schauen uns an, wie man dies in einem objektorientierten Programm umsetzen kann.

Zunächst erstellen wir eine `Auto`-Klasse, die als Basis für mehrere Klassen unterschiedlicher Autotypen dient.

abstract class Auto {
    void starten() {
        // Implementierung
        System.out.println("Auto gestartet");
    }

    void stoppen() {
        // Implementierung
        System.out.println("Motor gestoppt");
    }

    void rueckwaertsfahren() {
        // Implementierung
        System.out.println("Rückwärtsgang aktiviert");
    }

    abstract void autonomFahren();
}

Die Methoden `starten()`, `stoppen()` und `rueckwaertsfahren()` sind Methoden, die allen Autos gemeinsam sind. Daher ist ihre Implementierung bereits in der `Auto`-Klasse definiert. Ein bestimmter Autotyp kann jedoch eine eigene Implementierung für den autonomen Fahrmodus haben. Aus diesem Grund wird `autonomFahren()` als abstrakte Methode deklariert, die in den unterschiedlichen Klassen der verschiedenen Fahrzeugtypen jeweils auf eigene Weise implementiert werden kann.

class AutoTypA extends Auto {
    @Override
    void starten() {
        super.starten();
    }

    @Override
    void stoppen() {
        super.stoppen();
    }

    @Override
    void rueckwaertsfahren() {
        super.rueckwaertsfahren();
    }

    @Override
    void autonomFahren() {
        // Eigene Implementierung
        System.out.println("Autonomes Fahren Typ A aktiviert");
    }
}
class AutoTypB extends Auto {
    // ...alle ähnlichen Methoden

    @Override
    void autonomFahren() {
        // Eigene Implementierung
        // Unterschiedliche Implementierung als AutoTypB
        System.out.println("Autonomes Fahren Typ B aktiviert");
    }
}

Es ist wichtig zu beachten, dass, wenn eine Unterklasse nicht alle abstrakten Methoden implementiert, die in der abstrakten Klasse definiert sind, sie selbst als abstrakte Klasse deklariert werden muss.

Schnittstelle

Eine Schnittstelle ist eine Möglichkeit, einer Klasse mitzuteilen, welche Methoden sie implementieren muss. Nehmen wir das Beispiel eines Autos, das bestimmte grundlegende Funktionen hat: es kann starten, sich bewegen und stoppen. Diese grundlegenden Funktionen sind bei allen Autos gleich.

Wenn Sie also die Schnittstelle eines Autos in einer Klasse implementieren, müssen Sie alle Methoden implementieren, damit das Auto ordnungsgemäß und sicher funktioniert.

Ähnlich wie bei abstrakten Klassen können wir keine Objekte einer Schnittstelle instanziieren oder erstellen. Sie kann als vollständig abstrakte Klasse betrachtet werden, da sie ausschließlich abstrakte Methoden enthält, d.h. Methoden ohne Implementierungskörper.

Eine Schnittstelle wird mit dem Schlüsselwort `interface` erstellt.

interface AUTO {
    void starten();
    void stoppen();
    void bewegen();
}

Eine Schnittstelle wird implementiert, indem das Schlüsselwort `implements` bei der Definition einer Klasse verwendet wird.

class AutoTypB implements AUTO {
    @Override
    public void starten() {
        System.out.println("gestartet");
    }

    @Override
    public void stoppen() {
        System.out.println("gestoppt");
    }

    @Override
    public void bewegen() {
        System.out.println("fährt");
    }
}

Ähnlichkeit

Die Gemeinsamkeit von abstrakten Klassen und Schnittstellen ist die Tatsache, dass beide nicht als Objekte instanziert werden können.

Unterschiede

Abstrakte Klasse Schnittstelle
Vererbung und Implementierung Nur eine abstrakte Klasse kann von einer Klasse geerbt werden. Eine Klasse kann mehrere Schnittstellen implementieren.
Variablentypen Kann `final`, nicht-`final`, `static` und nicht-`static` Variablen haben. Kann nur `static` und `final` Variablen haben.
Methodentypen Kann sowohl abstrakte als auch nicht-abstrakte Methoden enthalten. Kann nur abstrakte Methoden enthalten, aber statische Methoden sind eine Ausnahme.
Zugriffsmodifikatoren Eine abstrakte Klasse kann einen Zugriffsmodifikator haben. Methodensignaturen in der Schnittstelle sind standardmäßig `public`. Eine Schnittstelle hat keinen Zugriffsmodifikator.
Konstruktoren und Destruktoren Kann Konstruktoren und Destruktoren deklarieren. Kann keine Konstruktoren oder Destruktoren deklarieren.
Geschwindigkeit Schnell Langsam

Unterschiede zwischen abstrakter Klasse und Schnittstelle

Wann verwendet man abstrakte Klassen und Schnittstellen?

Verwenden Sie abstrakte Klassen, wenn:

  • Sie einige gemeinsame Methoden und Felder für mehrere Klassen bereitstellen möchten.
  • Sie nicht-statische und nicht-finale Felder deklarieren möchten, um den Zustand des Objekts zu verändern, an das sie gebunden sind.

Verwenden Sie Schnittstellen, wenn:

  • Sie das Verhalten einer Klasse definieren möchten, die die Schnittstelle implementiert, aber es ist nicht relevant, wie die Implementierung aussieht.
  • Sie sicherstellen möchten, dass eine Klasse alle Methoden implementiert, um korrekt zu funktionieren.

Abschließende Bemerkungen

Schnittstellen werden vor allem für die Erstellung von APIs verwendet, da sie eine Struktur zur Implementierung von Funktionen bereitstellen können, ohne auf die konkrete Implementierung eingehen zu müssen.

Abstrakte Klassen werden im Allgemeinen genutzt, um gemeinsame abstrakte und nicht-abstrakte Methoden zwischen verschiedenen Klassen zu teilen, wobei die abstrakte Klasse erweitert wird, um die Wiederverwendbarkeit des Codes zu erhöhen.

Erfahren Sie mehr über Java mit Hilfe von Online-Kursen für Java. Bereiten Sie sich auf ein Vorstellungsgespräch zu Java vor? Hier sind einige Interviewfragen zur objektorientierten Programmierung.