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:
| Merkmal | Abstrakte Klasse | Interface |
|---|---|---|
| Instanzvariablen | Ja | Nur Konstanten |
| Konstruktoren | Ja | Nein |
| Mehrfachvererbung | Nein (einfaches extends) | Ja (mehrfaches implements) |
| Zugriffsmodifikatoren | Beliebig | Nur 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.
Aufgabe
EinfachLassen 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 einbrand-Feld (String) und einyear-Feld (int). Fügen Sie einen Konstruktor zur Initialisierung beider Felder, Getter-Methoden für jedes Feld und eine abstrakte MethodestartEngine()hinzu, die einen String zurückgibt. Fügen Sie außerdem eine konkrete MethodegetInfo()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()undcloseRoof(), die beide einen String zurückgeben.Car.java: Erstellen Sie eine Klasse, dieVehicleerweitert undConvertibleimplementiert. Ein Car hat ein zusätzlichesnumDoors-Feld (int). Verwenden Siesuper, um die geerbten Felder zu initialisieren. Implementieren SiestartEngine()so, dass es Folgendes zurückgibt:[brand] car engine started. Implementieren SieopenRoof()so, dass es[brand] roof openingzurückgibt, undcloseRoof()so, dass es[brand] roof closingzurückgibt.Motorcycle.java: Erstellen Sie eine Klasse, dieVehicleerweitert, aberConvertibleNICHT implementiert – Motorräder haben keine Dächer! Ein Motorcycle hat einhasSidecar-Feld (boolean). Implementieren SiestartEngine()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 vonstartEngine()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
Convertibleimplementiert, rufen SieopenRoof()gefolgt voncloseRoof()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 { ... }| Merkmal | Abstrakte Klasse | Interface |
|---|---|---|
| Instanzvariablen | Ja | Nur Konstanten |
| Konstruktoren | Ja | Nein |
| Mehrfachvererbung | Nein (einfaches extends) | Ja (mehrfaches implements) |
| Zugriffsmodifikatoren | Beliebig | Nur 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)
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenFelder (Attribute)Konstruktor-MethodeKonstruktor-ÜberladungRückblick – Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (extends)Das super-SchlüsselwortMethoden überschreiben (@Override)Konstruktor-VerkettungDie Object-KlasseEinfache & mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenZusammenfassung - Mitarbeiter-Hierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-InterfaceZusammenfassung – Benutzerdefiniertes Sortieren2Zugriffsmodifikatoren & Kapselung
Überblick über ZugriffsebenenGetter- und Setter-MethodenInformation HidingDas final-SchlüsselwortRückblick – Bankkonto-Manager5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenZusammenfassung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, geschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Sealed Classes (Java 17+)11Design Patterns Teil 1
Einführung in Design PatternsSingleton-PatternFactory-PatternBuilder-PatternObserver-PatternStrategy-Pattern14Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung3Klasseneigenschaften & Statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Zusammenfassung – Zähler & Utility6Interfaces & Abstrakte Klassen
Einführung in InterfacesInterfaces implementierenImplementierung mehrerer InterfacesDefault- & statische Methoden in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesZusammenfassung - Zahlungssystem