1. Abstrakte Klasse: Wiederholung der Grundlagen
Bevor wir zum Vergleich übergehen, frischen wir kurz auf, was eine abstrakte Klasse ist.
Eine abstrakte Klasse ist eine Klasse, die nicht direkt instanziiert werden kann (man kann nicht new Animal() schreiben), die aber sowohl normale (mit Implementierung) als auch abstrakte (ohne Implementierung) Methoden enthalten kann. Eine abstrakte Klasse wird oft als Grundlage für andere Klassen verwendet, die ihr Verhalten erben und/oder bestimmte Methoden implementieren müssen.
Wenn es in unserer Anwendung zum Beispiel verschiedene Arten von Transportmitteln gibt, können wir eine abstrakte Klasse Transport einführen:
public abstract class Transport {
private String model;
public Transport(String model) {
this.model = model;
}
public String getModel() {
return model;
}
// Abstrakte Methode – keine Implementierung, nur Signatur
public abstract void move();
// Normale Methode – Implementierung vorhanden
public void printInfo() {
System.out.println("Transportmodell: " + model);
}
}
Besonderheiten einer abstrakten Klasse:
- Kann Felder (Zustand) enthalten.
- Kann implementierte Methoden enthalten.
- Kann abstrakte Methoden enthalten (müssen in Unterklassen implementiert werden).
- Kann nicht direkt instanziiert werden.
- Wird für gemeinsames Verhalten und gemeinsamen Zustand verwendet.
2. Schnittstelle: Wiederholung der Grundlagen
Eine Schnittstelle ist eine Menge von Methoden, die eine Klasse implementieren muss. Eine Schnittstelle beschreibt keinen Zustand (sie kann keine normalen Felder haben, nur Konstanten) und enthält keine Methodenimplementierungen (bis Java 8). Eine Schnittstelle ist ein reiner Vertrag: „Wenn du mich implementierst, musst du Folgendes können.“
Beispiel für eine Schnittstelle:
public interface Movable {
void move(int x, int y);
}
Besonderheiten einer Schnittstelle:
- Enthält keinen Zustand (nur public static final Konstanten).
- Bis Java 8 – nur abstrakte Methoden (seit Java 8 gibt es default- und static-Methoden, dazu später mehr).
- Methoden sind standardmäßig immer public abstract.
- Eine Klasse kann mehrere Schnittstellen implementieren.
- Wird verwendet, um Fähigkeiten zu beschreiben, also „was eine Klasse kann“.
3. Vergleichstabelle: abstrakte Klasse vs. Schnittstelle
Es ist Zeit, diese beiden Werkzeuge direkt zu vergleichen! Hier eine übersichtliche Tabelle:
| Merkmal | Abstrakte Klasse | Schnittstelle |
|---|---|---|
| Syntax | |
|
| Instanziierbar? | Nein | Nein |
| Kann normale Methoden enthalten? | Ja | Bis Java 8 – nein, seit Java 8 – nur default/static |
| Kann abstrakte Methoden enthalten? | Ja | Ja (bis Java 8 sind alle Methoden abstrakt) |
| Kann Felder (Zustand) enthalten? | Ja (beliebige Felder) | Nur public static final (Konstanten) |
| Kann Konstruktoren enthalten? | Ja | Nein |
| Vererbung | Nur eine Basisklasse (abstrakt oder konkret) | Mehrere Schnittstellen können implementiert werden |
| Methodenmodifikatoren | Beliebige (public, protected, private) | Methoden sind standardmäßig public abstract. Seit Java 9 können private Methoden für die Nutzung innerhalb der Schnittstelle hinzugefügt werden |
| Vererbung durch | |
|
| Typische Verwendung | Gemeinsame Implementierung und Zustand | Beschreibung von Fähigkeiten und Rollen |
| Beispiele aus dem JDK | |
|
4. Wann eine Schnittstelle und wann eine abstrakte Klasse verwenden?
Eine Schnittstelle verwenden Sie, wenn:
- Sie beschreiben möchten, „was eine Klasse kann“, ohne sich darum zu kümmern, wie sie es tut.
- eine Klasse mehrere unabhängige Fähigkeiten haben soll.
- Beispiele: Comparable (kann verglichen werden), Serializable (kann serialisiert werden), Runnable (kann in einem Thread ausgeführt werden).
Eine abstrakte Klasse verwenden Sie, wenn:
- Sie eine gemeinsame Implementierung und einen gemeinsamen Zustand vorgeben möchten, den alle Unterklassen haben.
- alle Unterklassen bestimmte Felder oder Methoden mit Implementierung besitzen sollen.
- die Vererbung strikt „eins zu eins“ ist: Eine Klasse kann nur eine Klasse (konkret oder abstrakt) erweitern.
Lebensnahe Analogien
- Schnittstelle – das ist wie ein „Führerschein“: Wenn man ihn hat, darf man Auto fahren, aber niemand schreibt vor, welches Auto und wie genau man fährt.
- Abstrakte Klasse – das ist wie ein „allgemeiner Konstruktionsplan eines Autos“: Alle Autos haben Lenkrad, Pedale, Motor, aber jede Marke setzt die Details unterschiedlich um.
5. Beispiele aus der Java-Standardbibliothek
Schnittstelle: Comparable
public interface Comparable<T> {
int compareTo(T o);
}
Jede Klasse, die diese Schnittstelle implementiert, muss die Methode compareTo implementieren. Zum Beispiel String, Integer, LocalDate und viele andere.
Abstrakte Klasse: AbstractList
public abstract class AbstractList<E> implements List<E> {
// Standardimplementierung einiger List-Methoden
// Einige Methoden bleiben abstrakt
}
AbstractList implementiert bereits einen Teil des Verhaltens von Collections (z. B. Methoden zum Hinzufügen/Entfernen), lässt aber einige Methoden abstrakt, damit Unterklassen sie auf ihre Weise implementieren können.
6. Codebeispiele: praktischer Vergleich
Schnittstelle
Erstellen wir eine Schnittstelle und eine Klasse, die sie implementiert.
public interface Printable {
void print();
}
public class Document implements Printable {
@Override
public void print() {
System.out.println("Ich drucke ein Dokument...");
}
}
Abstrakte Klasse
Nun eine abstrakte Klasse und ihre Unterklasse.
public abstract class Machine {
public void turnOn() {
System.out.println("Die Maschine ist eingeschaltet.");
}
public abstract void work();
}
public class Printer extends Machine {
@Override
public void work() {
System.out.println("Der Drucker druckt...");
}
}
Klasse implementiert beides: Schnittstelle und abstrakte Basisklasse
public class SmartPrinter extends Machine implements Printable {
@Override
public void work() {
System.out.println("Der Smart-Drucker arbeitet...");
}
@Override
public void print() {
System.out.println("Der Smart-Drucker druckt...");
}
}
7. Mehrfachimplementierung von Schnittstellen: warum das großartig ist
In Java kann eine Klasse nur eine Klasse (abstrakt oder konkret) erweitern, aber beliebig viele Schnittstellen implementieren! Das ermöglicht flexible, erweiterbare Architekturen.
public interface Scannable {
void scan();
}
public class MultiFunctionPrinter extends Machine implements Printable, Scannable {
@Override
public void work() {
System.out.println("Der Multifunktionsdrucker arbeitet...");
}
@Override
public void print() {
System.out.println("Der Multifunktionsdrucker druckt...");
}
@Override
public void scan() {
System.out.println("Der Multifunktionsdrucker scannt...");
}
}
Wann was wählen?
- Wenn Sie Basisfunktionalität mit gemeinsamem Zustand (z. B. Felder) entwerfen, verwenden Sie eine abstrakte Klasse.
- Wenn Sie Objekten „Fähigkeits-Labels“ geben möchten (z. B. „kann drucken“, „kann verglichen werden“, „kann serialisiert werden“) – verwenden Sie Schnittstellen.
- Wenn Sie unsicher sind – beginnen Sie mit einer Schnittstelle. In Java gilt das als gute Praxis: Schnittstellen bieten große Flexibilität und Erweiterbarkeit.
8. Typische Fehler und Fallstricke
Fehler Nr. 1: Sie versuchen, von mehreren Klassen zu erben – Java lässt das nicht zu!
Eine Klasse kann nur eine Klasse erben, aber viele Schnittstellen implementieren. Zum Beispiel ist class A extends B, C ein Fehler, aber class A extends B implements X, Y, Z ist in Ordnung.
Fehler Nr. 2: Felder von Schnittstelle und Klasse werden verwechselt.
In einer Schnittstelle können nur Konstanten (public static final) deklariert werden. Ein normaler Zustand wie private int count; ist nicht erlaubt – der Compiler stoppt Sie sofort.
Fehler Nr. 3: Nicht alle Methoden der Schnittstelle wurden implementiert.
Wenn eine Klasse auch nur eine Methode der Schnittstelle nicht implementiert, muss sie als abstract deklariert werden, sonst meldet der Compiler einen Fehler.
Fehler Nr. 4: Sie versuchen, eine Instanz einer Schnittstelle oder einer abstrakten Klasse zu erstellen.
Beide Typen sind „Halbfertigprodukte“. Man kann sie nur erweitern, aber nicht direkt instanziieren:
Printable p = new Printable(); // Fehler!
Machine m = new Machine(); // Fehler!
Fehler Nr. 5: Sie denken, dass eine Schnittstelle einen Konstruktor haben kann.
Schnittstellen können keine Konstruktoren haben, da sie keinen Objektzustand beschreiben. Nur Klassen (konkrete und abstrakte) haben Konstruktoren.
GO TO FULL VERSION