Was ist die Standardmethode?
Nach der Veröffentlichung von Java 8 können der Schnittstelle neue Methoden hinzugefügt werden, die Schnittstelle bleibt jedoch weiterhin mit ihrer Implementierungsklasse kompatibel. Dies ist wichtig, da die von Ihnen entwickelte Bibliothek möglicherweise von mehreren Entwicklern häufig verwendet wird. Vor Java 8 bestand nach der Veröffentlichung einer Schnittstelle in einer Klassenbibliothek die Gefahr, dass Anwendungen, die diese Schnittstelle implementierten, mit der neuen Version der Schnittstelle abstürzen, wenn der Schnittstelle eine neue Methode hinzugefügt wurde.
Besteht diese Gefahr bei Java 8 nicht? Die Antwort ist nein.
Das Hinzufügen von Standardmethoden zu einer Schnittstelle kann dazu führen, dass einige Implementierungsklassen nicht verfügbar sind.
Schauen wir uns zunächst die Details der Standardmethode an.
In Java 8 können Methoden in Schnittstellen implementiert werden (statische Methoden in Java 8 können auch in Schnittstellen implementiert werden, aber das ist ein anderes Thema). Die in der Schnittstelle implementierte Methode wird als Standardmethode bezeichnet und mit dem Schlüsselwort default als Modifikator identifiziert. Wenn eine Klasse eine Schnittstelle implementiert, kann sie Methoden implementieren, die bereits in der Schnittstelle implementiert sind, dies ist jedoch nicht erforderlich. Diese Klasse erbt die Standardmethode. Aus diesem Grund muss bei einer Änderung der Schnittstelle die Implementierungsklasse nicht geändert werden.
Was ist mit Mehrfacherben?
Wenn eine Klasse mehr als eine (z. B. zwei) Schnittstellen implementiert und diese Schnittstellen dieselbe Standardmethode haben, werden die Dinge sehr kompliziert. Welche Standardmethode erbt die Klasse? Weder! In diesem Fall muss die Klasse selbst (entweder direkt oder eine Klasse weiter oben im Vererbungsbaum) die Standardmethode implementieren.
Das Gleiche gilt, wenn eine Schnittstelle die Standardmethode implementiert und eine andere Schnittstelle die Standardmethode als abstrakt deklariert. Java 8 versucht, Mehrdeutigkeiten zu vermeiden und die Genauigkeit beizubehalten. Wenn eine Methode in mehreren Schnittstellen deklariert wird, wird keine der Standardimplementierungen geerbt und Sie erhalten einen Fehler bei der Kompilierung.
Wenn Sie Ihre Klasse jedoch kompiliert haben, treten beim Kompilieren keine Fehler auf. Zu diesem Zeitpunkt ist Java 8 inkonsistent. Es hat seine eigenen Gründe, und es gibt verschiedene Gründe. Ich möchte es hier nicht im Detail erklären oder ausführlich diskutieren (weil: Die Version wurde veröffentlicht, die Diskussionszeit ist zu lang und diese Plattform hatte es noch nie so eine Diskussion).
1. Angenommen, Sie haben zwei Schnittstellen und eine Implementierungsklasse.
2. Eine der Schnittstellen implementiert eine Standardmethode m().
3. Kompilieren Sie die Schnittstelle und die Implementierungsklasse zusammen.
4. Ändern Sie die Schnittstelle, die die m()-Methode nicht enthält, und deklarieren Sie die m()-Methode als abstrakt.
5. Kompilieren Sie die geänderte Schnittstelle separat neu.
6. Führen Sie die Implementierungsklasse aus.
1. Ändern Sie die Schnittstelle, die die abstrakte Methode m() enthält, und erstellen Sie eine Standardimplementierung.
2. Kompilieren Sie die geänderte Schnittstelle
3. Klasse ausführen: Fehlgeschlagen.
Wenn zwei Schnittstellen eine Standardimplementierung für dieselbe Methode bereitstellen, kann diese Methode nur dann aufgerufen werden, wenn die Implementierungsklasse auch die Standardmethode implementiert (entweder direkt oder durch eine übergeordnete Klasse im Vererbungsbaum).
Beispielcode:
Um das obige Beispiel zu veranschaulichen, habe ich ein Testverzeichnis für C.java erstellt. Darunter befinden sich drei Unterverzeichnisse zum Speichern von I1.java und I2.java. Das Testverzeichnis enthält den Quellcode C.java der Klasse C. Das Basisverzeichnis enthält die Version der Schnittstelle, die kompiliert und ausgeführt werden kann. I1 enthält die m()-Methode mit Standardimplementierung und I2 enthält keine Methoden.
Die Implementierungsklasse enthält die Hauptmethode, damit wir sie in unseren Tests ausführen können. Es prüft, ob Befehlszeilenparameter vorhanden sind, sodass wir problemlos Tests durchführen können, indem wir m() aufrufen und nicht m() aufrufen.
Kopieren Sie den Codecode wie folgt:
~/github/test$ cat C.java
Die öffentliche Klasse C implementiert I1, I2 {
public static void main(String[] args) {
C c = neues C();
if(args.length == 0){
cm();
}
}
}
~/github/test$ cat base/I1.java
öffentliche Schnittstelle I1 {
Standard void m(){
System.out.println("Hallo Schnittstelle 1");
}
}
~/github/test$ cat base/I2.java
öffentliche Schnittstelle I2 {
}
Verwenden Sie zum Kompilieren und Ausführen die folgende Befehlszeile:
Kopieren Sie den Code wie folgt:~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
Hallo Schnittstelle 1
Das kompatible Verzeichnis enthält die I2-Schnittstelle mit der abstrakten Methode m() und die unveränderte I1-Schnittstelle.
Kopieren Sie den Code wie folgt:~/github/test$ cat kompatibel/I2.java
öffentliche Schnittstelle I2 {
void m();
}
Dies kann nicht zum Kompilieren der Klasse C verwendet werden:
Kopieren Sie den Code wie folgt:~/github/test$ javac -cp .:kompatibles C.java
C.java:1: Fehler: C ist nicht abstrakt und überschreibt die abstrakte Methode m() in I2 nicht
Die öffentliche Klasse C implementiert I1, I2 {
^
1 Fehler
Die Fehlermeldung ist sehr präzise. Da wir die C.class in der vorherigen Kompilierung erhalten haben, erhalten wir beim Kompilieren der Schnittstellen im kompatiblen Verzeichnis immer noch zwei Schnittstellen, die die Implementierungsklasse ausführen können:
Kopieren Sie den Codecode wie folgt:
~/github/test$ javac kompatibel/I*.java
~/github/test$ java -cp .:kompatibles C
Hallo Schnittstelle 1
Das dritte Verzeichnis mit dem Namen „falsch“ enthält die I2-Schnittstelle, die auch die m()-Methode definiert:
Kopieren Sie den Codecode wie folgt:
~/github/test$ cat falsch/I2.java
öffentliche Schnittstelle I2 {
Standard void m(){
System.out.println("Hallo Schnittstelle 2");
}
}
Wir sollten uns die Mühe machen, es zu kompilieren. Obwohl die Methode m() zweimal definiert ist, kann die Implementierungsklasse weiterhin ausgeführt werden, solange sie die definierte Methode nicht mehrmals aufruft. Solange wir jedoch die Methode m() aufrufen, schlägt sie sofort fehl. Hier sind die Befehlszeilenparameter, die wir verwenden:
Kopieren Sie den Codecode wie folgt:
~/github/test$ javac false/*.java
~/github/test$ java -cp .:wrong C
Ausnahme im Thread „main“ java.lang.InkompatibleClassChangeError: Konflikt
Standardmethoden: I1.m I2.m
bei Cm(C.java)
bei C.main(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/test$
abschließend
Wenn Sie eine Klassenbibliothek, die einer Schnittstelle eine Standardimplementierung hinzufügt, in die Java 8-Umgebung portieren, treten im Allgemeinen keine Probleme auf. Das dachten sich zumindest die Entwickler der Java8-Klassenbibliothek, als sie den Sammlungsklassen Standardmethoden hinzufügten. Anwendungen, die Ihre Bibliothek verwenden, sind immer noch auf Java 7-Bibliotheken angewiesen, die keine Standardmethoden haben. Bei der Verwendung und Änderung mehrerer verschiedener Klassenbibliotheken besteht eine geringe Wahrscheinlichkeit, dass Konflikte auftreten. Wie kann dies vermieden werden?
Gestalten Sie Ihre Klassenbibliothek wie zuvor. Nehmen Sie es nicht auf die leichte Schulter, wenn Sie sich auf die Standardmethode verlassen. Nicht als letzten Ausweg verwenden. Wählen Sie Methodennamen mit Bedacht aus, um Konflikte mit anderen Schnittstellen zu vermeiden. Wir werden lernen, wie man diese Funktion für die Entwicklung in der Java-Programmierung nutzt.