Default- & statische Methoden in Interfaces
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy — Lektion 37 von 87.
Ursprünglich konnten Interfaces nur abstrakte Methoden ohne Implementierung enthalten. Seit Java 8 haben Interfaces die Möglichkeit erhalten, Default-Methoden und statische Methoden aufzunehmen, was sie flexibler macht.
Eine Default-Methode stellt eine Implementierung direkt im Interface unter Verwendung des Schlüsselworts default bereit. Klassen, die das Interface implementieren, erben dieses Verhalten automatisch, können es jedoch bei Bedarf überschreiben:
public interface Drawable {
void draw();
default void display() {
System.out.println("Displaying on screen...");
draw();
}
}Jede Klasse, die Drawable implementiert, erhält die display()-Methode kostenlos. Dies ist besonders nützlich, um bestehenden Schnittstellen neue Methoden hinzuzufügen, ohne Klassen zu beeinträchtigen, die diese bereits implementieren.
Statische Methoden in Interfaces funktionieren wie statische Methoden in Klassen. Sie gehören zum Interface selbst und werden über den Namen des Interfaces aufgerufen:
public interface MathOperations {
static int add(int a, int b) {
return a + b;
}
}
// Verwendung
int result = MathOperations.add(5, 3);Statische Interface-Methoden können nicht überschrieben werden und werden nicht von implementierenden Klassen geerbt. Sie sind nützlich für Utility-Methoden, die mit dem Zweck des Interfaces in Verbindung stehen.
Wenn eine Klasse zwei Interfaces mit der gleichen Default-Methoden-Signatur implementiert, muss die Klasse diese Methode überschreiben, um den Konflikt zu lösen.
Aufgabe
EinfachLassen Sie uns ein Nachrichtensystem erstellen, das sowohl Standard- (default) als auch statische Methoden in Schnittstellen (Interfaces) demonstriert. Sie werden ein Interface mit integrierter Funktionalität erstellen, die implementierende Klassen direkt verwenden oder bei Bedarf anpassen können.
Sie werden Ihren Code über vier Dateien organisieren:
Messenger.java: Definieren Sie ein Interface, das einen beliebigen Nachrichtendienst repräsentiert. Es sollte eine abstrakte MethodesendMessage(String message)deklarieren, die void zurückgibt. Fügen Sie eine default method namenssendWithTimestamp(String message)hinzu, die Folgendes ausgibt:[TIMESTAMP]gefolgt vom Aufruf vonsendMessage(message). Fügen Sie außerdem eine static method namensformatMessage(String message)hinzu, die die Nachricht in Großbuchstaben umgewandelt zurückgibt.EmailMessenger.java: Erstellen Sie eine Klasse, dieMessengerimplementiert. Sie hat ein Feldrecipient(String). Fügen Sie einen Konstruktor hinzu, um den Empfänger zu initialisieren. Implementieren SiesendMessage(), um Folgendes auszugeben:Email to [recipient]: [message]. Diese Klasse verwendet die standardmäßigesendWithTimestamp()-Methode unverändert.SMSMessenger.java: Erstellen Sie eine weitere Klasse, dieMessengerimplementiert. Sie hat ein FeldphoneNumber(String). Fügen Sie einen Konstruktor hinzu, um die Telefonnummer zu initialisieren. Implementieren SiesendMessage(), um Folgendes auszugeben:SMS to [phoneNumber]: [message]. Überschreiben Sie die standardmäßigesendWithTimestamp()-Methode, um Folgendes auszugeben:[URGENT]gefolgt vom Aufruf vonsendMessage(message), anstatt das Standard-Zeitstempel-Verhalten zu verwenden.Main.java: Bringen Sie alles zusammen, um sowohl Standard- als auch statische Interface-Methoden zu demonstrieren. Sie erhalten drei Eingaben: einen E-Mail-Empfänger, eine Telefonnummer und eine Nachricht. Verwenden Sie zuerst die statische MethodeMessenger.formatMessage(), um die Nachricht zu formatieren und zu speichern. Erstellen Sie dann einen EmailMessenger und einen SMSMessenger. Rufen Sie für jeden MessengersendWithTimestamp()mit der formatierten Nachricht auf – beachten Sie, wie der EmailMessenger die Standardimplementierung verwendet, während der SMSMessenger seine überschriebene Version nutzt.
Sie erhalten drei Eingaben: den E-Mail-Empfänger (String), die Telefonnummer (String) und die Nachricht (String).
Ihre Ausgabe sollte insgesamt vier Zeilen zeigen – zwei für jeden Messenger. Der EmailMessenger zeigt das Standard-Zeitstempel-Präfix, während der SMSMessenger sein benutzerdefiniertes Dringlichkeits-Präfix zeigt. Beide verwenden die statisch formatierte (großgeschriebene) Nachricht!
Spickzettel
Ab Java 8 können Schnittstellen zusätzlich zu abstrakten Methoden auch Default-Methoden und statische Methoden enthalten.
Default-Methoden stellen eine Implementierung direkt in der Schnittstelle unter Verwendung des Schlüsselworts default bereit. Klassen, die die Schnittstelle implementieren, erben dieses Verhalten automatisch, können es jedoch bei Bedarf überschreiben:
public interface Drawable {
void draw();
default void display() {
System.out.println("Displaying on screen...");
draw();
}
}Statische Methoden in Schnittstellen gehören zur Schnittstelle selbst und werden über den Schnittstellennamen aufgerufen. Sie können nicht überschrieben werden und werden nicht von implementierenden Klassen geerbt:
public interface MathOperations {
static int add(int a, int b) {
return a + b;
}
}
// Verwendung
int result = MathOperations.add(5, 3);Wenn eine Klasse zwei Schnittstellen mit derselben Signatur für eine Default-Methode implementiert, muss die Klasse diese Methode überschreiben, um den Konflikt zu lösen.
Probier es selbst
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Eingaben lesen
String emailRecipient = scanner.nextLine();
String phoneNumber = scanner.nextLine();
String message = scanner.nextLine();
// TODO: Verwende Messenger.formatMessage(), um die Nachricht zu formatieren und zu speichern
// TODO: Erstelle einen EmailMessenger mit dem email recipient
// TODO: Erstelle einen SMSMessenger mit der phoneNumber
// TODO: Rufe sendWithTimestamp() auf dem EmailMessenger mit der formatierten Nachricht auf
// TODO: Rufe sendWithTimestamp() auf dem SMSMessenger mit der formatierten Nachricht auf
}
}
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