Note
🎯 TL;DR
Seit Java8 können Methoden in Interfaces auch fertig implementiert sein: Sogenannte Default-Methoden.
Dazu werden die Methoden mit dem neuen Schlüsselwort default
gekennzeichnet. Die Implementierung wird an die das Interface
implementierenden Klassen (oder Interfaces) vererbt und kann bei
Bedarf überschrieben werden.
Da eine Klasse von einer anderen Klasse erben darf, aber mehrere Interfaces implementieren kann, könnte es zu einer Mehrfachvererbung einer Methode kommen: Eine Methode könnte beispielsweise in verschiedenen Interfaces als Default-Methode angeboten werden, und wenn eine Klasse diese Interfaces implementiert, steht eine Methode mit der selben Signatur auf einmal mehrfach zur Verfügung. Dies muss (u.U. manuell) aufgelöst werden.
Auflösung von Mehrfachvererbung:
- Regel 1: Klassen gewinnen
- Regel 2: Sub-Interfaces gewinnen
- Regel 3: Methode explizit auswählen
Aktuell ist der Unterschied zu abstrakten Klassen: Interfaces können keinen Zustand haben, d.h. keine Attribute/Felder.
interface Klausur {
void anmelden(Studi s);
void abmelden(Studi s);
}=> Nachträglich noch void schreiben(Studi s); ergänzen?
Wenn ein Interface nachträglich erweitert wird, müssen alle Kunden (also alle Klassen, die das Interface implementieren) auf die neuen Signaturen angepasst werden. Dies kann viel Aufwand verursachen und API-Änderungen damit unmöglich machen.
Seit Java8 können Interfaces auch Methoden implementieren. Es gibt zwei Varianten: Default-Methoden und statische Methoden.
interface Klausur {
void anmelden(Studi s);
void abmelden(Studi s);
default void schreiben(Studi s) {
... // Default-Implementierung
}
default void wuppie() {
throw new java.lang.UnsupportedOperationException();
}
}Methoden können in Interfaces seit Java8 implementiert werden. Für
Default-Methoden muss das Schlüsselwort default vor die Signatur
gesetzt werden. Klassen, die das Interface implementieren, können diese
Default-Implementierung erben oder selbst neu implementieren
(überschreiben). Alternativ kann die Klasse eine Default-Methode neu
deklarieren und wird damit zur abstrakten Klasse.
Dies ähnelt abstrakten Klassen. Allerdings kann in abstrakten Klassen neben dem Verhalten (implementierten Methoden) auch Zustand über die Attribute gespeichert werden.
Drei Regeln zum Auflösen bei Konflikten:
- Klassen gewinnen: Methoden aus Klasse oder Superklasse haben höhere Priorität als Default-Methoden
- Sub-Interfaces gewinnen: Methode aus am meisten spezialisiertem
Interface mit Default-Methode wird gewählt Beispiel: Wenn
B extends Adann istBspezialisierter alsA - Sonst: Klasse muss Methode explizit auswählen: Methode
überschreiben und gewünschte (geerbte) Variante aufrufen:
X.super.m(...)(Xist das gewünschte Interface)
Auf den folgenden Folien wird dies anhand kleiner Beispiele verdeutlicht.
interface A {
default String hello() { return "A"; }
}
class C {
public String hello() { return "C"; }
}
class E extends C implements A {}
/** Mehrfachvererbung: 1. Klassen gewinnen */
public class DefaultTest1 {
public static void main(String... args) {
String e = new E().hello();
}
}Demo: defaultmethods.rule1.DefaultTest1
Die Klasse E erbt sowohl von Klasse C als auch vom Interface A die
Methode hello() (Mehrfachvererbung). In diesem Fall “gewinnt” die
Implementierung aus Klasse C.
1. Regel: Klassen gewinnen immer. Deklarationen einer Methode in einer Klasse oder einer Oberklasse haben Vorrang von allen Default-Methoden.
interface A {
default String hello() { return "A"; }
}
interface B extends A {
@Override default String hello() { return "B"; }
}
class D implements A, B {}
/** Mehrfachvererbung: 2. Sub-Interfaces gewinnen */
public class DefaultTest2 {
public static void main(String... args) {
String e = new D().hello();
}
}Demo: defaultmethods.rule2.DefaultTest2
Die Klasse D erbt sowohl vom Interface A als auch vom Interface B
die Methode hello() (Mehrfachvererbung). In diesem Fall “gewinnt” die
Implementierung aus Klasse B: Interface B ist spezialisierter als
A.
2. Regel: Falls Regel 1 nicht zutrifft, gewinnt die Default-Methode, die am meisten spezialisiert ist.
interface A {
default String hello() { return "A"; }
}
interface B {
default String hello() { return "B"; }
}
class D implements A, B {
@Override public String hello() { return A.super.hello(); }
}
/** Mehrfachvererbung: 3. Methode explizit auswählen */
public class DefaultTest3 {
public static void main(String... args) {
String e = new D().hello();
}
}Demo: defaultmethods.rule3.DefaultTest3
Die Klasse D erbt sowohl vom Interface A als auch vom Interface B
die Methode hello() (Mehrfachvererbung). In diesem Fall muss zur
Auflösung die Methode in D neu implementiert werden und die gewünschte
geerbte Methode explizit aufgerufen werden. (Wenn dies unterlassen wird,
führt das selbst bei Nicht-Nutzung der Methode hello() zu einem
Compiler-Fehler!)
Achtung: Der Aufruf der Default-Methode aus Interface A erfolgt mit
A.super.hello(); (nicht einfach durch A.hello();)!
3. Regel: Falls weder Regel 1 noch 2 zutreffen bzw. die Auflösung noch uneindeutig ist, muss man manuell durch die explizite Angabe der gewünschten Methode auflösen.
interface A {
default String hello() { return "A"; }
}
interface B extends A {
@Override default String hello() { return "B"; }
}
class C implements B {
@Override public String hello() { return "C"; }
}
class D extends C implements A, B {}
/** Quiz Mehrfachvererbung */
public class DefaultTest {
public static void main(String... args) {
String e = new D().hello(); // ???
}
}Die Klasse D erbt sowohl von Klasse C als auch von den Interfaces
A und B die Methode hello() (Mehrfachvererbung). In diesem Fall
“gewinnt” die Implementierung aus Klasse C: Klassen gewinnen immer
(Regel 1).
Beispiel: defaultmethods.quiz.DefaultTest
public interface Collection<E> extends Iterable<E> {
boolean add(E e);
...
}
public class Collections {
private Collections() { }
public static <T> boolean addAll(Collection<? super T> c, T... elements) {...}
...
}Typisches Pattern in Java: Interface plus Utility-Klasse
(Companion-Klasse) mit statischen Hilfsmethoden zum einfacheren Umgang
mit Instanzen des Interfaces (mit Objekten, deren Klasse das Interface
implementiert). Beispiel: Collections ist eine Hilfs-Klasse zum Umgang
mit Collection-Objekten.
Seit Java8 können in Interfaces neben Default-Methoden auch statische Methoden implementiert werden.
Die Hilfsmethoden können jetzt ins Interface wandern => Utility-Klassen werden obsolet … Aus Kompatibilitätsgründen würde man die bisherige Companion-Klasse weiterhin anbieten, wobei die Implementierungen auf die statischen Methoden im Interface verweisen (SKIZZE, nicht real!):
public interface CollectionX<E> extends Iterable<E> {
boolean add(E e);
static <T> boolean addAll(CollectionX<? super T> c, T... elements) { ... }
...
}
public class CollectionsX {
public static <T> boolean addAll(CollectionX<? super T> c, T... elements) {
return CollectionX.addAll(c, elements); // Verweis auf Interface
}
...
}-
Abstrakte Klassen: Schnittstelle und Verhalten und Zustand
-
Interfaces:
- vor Java 8 nur Schnittstelle
- ab Java 8 Schnittstelle und Verhalten
Unterschied zu abstrakten Klassen: Kein Zustand, d.h. keine Attribute
- Design:
- Interfaces sind beinahe wie abstrakte Klassen, nur ohne Zustand
- Klassen können nur von einer (abstrakten) Klasse erben, aber viele Interfaces implementieren
Seit Java8: Interfaces mit Implementierung: Default-Methoden
- Methoden mit dem Schlüsselwort
defaultkönnen Implementierung im Interface haben - Die Implementierung wird vererbt und kann bei Bedarf überschrieben werden
- Auflösung von Mehrfachvererbung:
- Regel 1: Klassen gewinnen
- Regel 2: Sub-Interfaces gewinnen
- Regel 3: Methode explizit auswählen
- Unterschied zu abstrakten Klassen: Kein Zustand
- Oracle Corporation (2024)
- Urma, Fusco, und Mycroft (2014, Kap. 9)
Tip
✅ Lernziele
- k2: Ich weiss, dass in Interfaces Default-Methoden erstellt werden können
- k2: Ich kann den Unterschied zwischen Interfaces mit Default-Methoden und abstrakten Klassen erklären
- k2: Ich verstehe das Problem der Mehrfachvererbung bei Interfaces mit Default-Methoden
- k3: Ich kann Interfaces mit Default-Methoden erstellen und einsetzen
- k3: Ich habe die Regeln zum Auflösen der Mehrfachvererbung verstanden und kann sie in der Praxis nutzen
🏅 Challenges
Erklären Sie die Code-Schnipsel in der Vorgabe und die jeweils entstehenden Ausgaben.
Note
👀 Quellen
Oracle Corporation. 2024. „The Java Tutorials“. 2024. https://docs.oracle.com/javase/tutorial/.
Urma, R.-G., M. Fusco, und A. Mycroft. 2014. Java 8 in Action: Lambdas, Streams, and Functional-Style Programming. Manning Publications.
Unless otherwise noted, this work is licensed under CC BY-SA 4.0.
Last modified: df56b1c (lecture: remove explicit link to pdf version, 2025-07-23)
