Menu
Coddy logo textTech

Abstrakte Klassen vs. Interfaces

Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy — Lektion 38 von 87.

Nachdem Sie nun sowohl abstrakte Klassen als auch Interfaces verstehen, stellt sich eine häufige Frage: Wann sollte man das eine dem anderen vorziehen? Die Wahl hängt davon ab, was Sie zu modellieren versuchen.

Verwenden Sie eine abstrakte Klasse, wenn Klassen eine gemeinsame Basis mit geteiltem Zustand oder Verhalten teilen. Abstrakte Klassen können Instanzvariablen, Konstruktoren und eine Mischung aus abstrakten und konkreten Methoden haben:

public abstract class Animal {
    protected String name;  // gemeinsamer Zustand
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void sleep() {  // gemeinsames Verhalten
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound();  // muss implementiert werden
}

Verwenden Sie ein Interface, wenn Sie eine Fähigkeit definieren möchten, die nicht miteinander verwandte Klassen teilen können. Interfaces konzentrieren sich darauf, was ein Objekt tun kann, nicht darauf, was es ist:

public interface Flyable {
    void fly();
}

// Nicht verwandte Klassen können diese Fähigkeit teilen
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }

Hier ist ein kurzer Vergleich:

MerkmalAbstrakte KlasseInterface
InstanzvariablenJaNur Konstanten
KonstruktorenJaNein
MehrfachvererbungNein (einfaches extends)Ja (mehrfaches implements)
ZugriffsmodifikatorenBeliebigNur public (für abstrakte Methoden)

Ein praktischer Richtwert: Wenn Sie feststellen, dass Sie eine abstrakte Klasse mit ausschließlich abstrakten Methoden und ohne Zustand erstellen, ist ein Interface wahrscheinlich die bessere Wahl.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Fahrzeugsystem bauen, das demonstriert, wann abstrakte Klassen im Gegensatz zu Interfaces verwendet werden sollten. Sie werden Fahrzeuge modellieren, die einen gemeinsamen Zustand und ein gemeinsames Verhalten über eine abstrakte Klasse teilen, während optionale Fähigkeiten über Interfaces hinzugefügt werden.

Sie werden Ihren Code über fünf Dateien organisieren:

  • Vehicle.java: Erstellen Sie eine abstrakte Klasse, die als Grundlage für alle Fahrzeuge dient. Jedes Fahrzeug hat ein brand-Feld (String) und ein year-Feld (int). Fügen Sie einen Konstruktor zur Initialisierung beider Felder, Getter-Methoden für jedes Feld und eine abstrakte Methode startEngine() hinzu, die einen String zurückgibt. Fügen Sie außerdem eine konkrete Methode getInfo() hinzu, die Folgendes zurückgibt: [brand] ([year]). Dies ist ein perfekter Anwendungsfall für eine abstrakte Klasse – Fahrzeuge teilen einen Zustand (Marke, Jahr) und ein gewisses Verhalten (getInfo), aber jedes startet seinen Motor anders.
  • Convertible.java: Definieren Sie ein Interface für Fahrzeuge, die ihr Dach umbauen können. Diese Fähigkeit ist nicht daran gebunden, was ein Fahrzeug ist – es ist etwas, das bestimmte Fahrzeuge tun können. Deklarieren Sie zwei Methoden: openRoof() und closeRoof(), die beide einen String zurückgeben.
  • Car.java: Erstellen Sie eine Klasse, die Vehicle erweitert und Convertible implementiert. Ein Car hat ein zusätzliches numDoors-Feld (int). Verwenden Sie super, um die geerbten Felder zu initialisieren. Implementieren Sie startEngine() so, dass es Folgendes zurückgibt: [brand] car engine started. Implementieren Sie openRoof() so, dass es [brand] roof opening zurückgibt, und closeRoof() so, dass es [brand] roof closing zurückgibt.
  • Motorcycle.java: Erstellen Sie eine Klasse, die Vehicle erweitert, aber Convertible NICHT implementiert – Motorräder haben keine Dächer! Ein Motorcycle hat ein hasSidecar-Feld (boolean). Implementieren Sie startEngine() so, dass es Folgendes zurückgibt: [brand] motorcycle engine roaring.
  • Main.java: Bringen Sie alles zusammen, um den Unterschied zwischen abstrakten Klassen und Interfaces zu zeigen. Sie erhalten vier Eingaben: eine Automarke, ein Baujahr des Autos, eine Motorradmarke und ein Baujahr des Motorrads.

    Erstellen Sie ein Car (mit 4 Türen) und ein Motorcycle (ohne Beiwagen). Demonstrieren Sie zuerst das gemeinsame Verhalten der abstrakten Klasse, indem Sie getInfo() für beide Fahrzeuge ausgeben – beide erben dies von Vehicle. Geben Sie dann das Ergebnis von startEngine() für jedes Fahrzeug aus – beachten Sie, wie jeder Fahrzeugtyp dies unterschiedlich implementiert.

    Demonstrieren Sie schließlich die Interface-Fähigkeit: Da nur das Car Convertible implementiert, rufen Sie openRoof() gefolgt von closeRoof() nur für das Auto auf und geben Sie die Ergebnisse aus.

Sie erhalten vier Eingaben: Automarke (String), Baujahr des Autos (int), Motorradmarke (String) und Baujahr des Motorrads (int).

Ihre Ausgabe sollte insgesamt sechs Zeilen umfassen. Beachten Sie, wie beide Fahrzeuge den Zustand der abstrakten Klasse und die Methode getInfo() teilen, aber nur das Car die Cabrio-Fähigkeit besitzt – dies veranschaulicht, wann welcher Ansatz zu verwenden ist!

Spickzettel

Verwenden Sie eine abstrakte Klasse, wenn Klassen eine gemeinsame Basis mit gemeinsamem Zustand oder Verhalten teilen. Abstrakte Klassen können Instanzvariablen, Konstruktoren und eine Mischung aus abstrakten und konkreten Methoden haben:

public abstract class Animal {
    protected String name;  // gemeinsamer Zustand
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void sleep() {  // gemeinsames Verhalten
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound();  // muss implementiert werden
}

Verwenden Sie ein Interface, wenn Sie eine Fähigkeit definieren möchten, die nicht verwandte Klassen teilen können. Interfaces konzentrieren sich darauf, was ein Objekt tun kann, nicht darauf, was es ist:

public interface Flyable {
    void fly();
}

// Nicht verwandte Klassen können diese Fähigkeit teilen
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }
MerkmalAbstrakte KlasseInterface
InstanzvariablenJaNur Konstanten
KonstruktorenJaNein
MehrfachvererbungNein (einfaches extends)Ja (mehrfaches implements)
ZugriffsmodifikatorenBeliebigNur public (für abstrakte Methoden)

Leitfaden: Wenn Sie eine abstrakte Klasse mit nur abstrakten Methoden und ohne Zustand erstellen, ist ein Interface wahrscheinlich die bessere Wahl.

Probier es selbst

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Eingaben lesen
        String carBrand = scanner.nextLine();
        int carYear = scanner.nextInt();
        scanner.nextLine(); // Zeilenumbruch konsumieren
        String motorcycleBrand = scanner.nextLine();
        int motorcycleYear = scanner.nextInt();
        
        // TODO: Erstelle ein Car mit 4 Türen
        
        // TODO: Erstelle ein Motorcycle ohne Beiwagen (false)
        
        // TODO: Gib getInfo() für beide Fahrzeuge aus (demonstriert das Verhalten der gemeinsamen abstrakten Klasse)
        
        // TODO: Gib startEngine() für beide Fahrzeuge aus (demonstriert unterschiedliche Implementierungen)
        
        // TODO: Gib openRoof() und closeRoof() nur für das Auto aus (demonstriert Interface-Funktionalität)
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung