Eine der nützlichsten Funktionen von Java 8 sind die neuen default
Methoden für Schnittstellen. Es gibt im Wesentlichen zwei Gründe (es kann andere geben), warum sie eingeführt wurden:
- Bereitstellung der tatsächlichen Standardimplementierungen. Beispiel:
Iterator.remove()
- Berücksichtigung der JDK-API-Evolution. Beispiel:
Iterable.forEach()
Aus Sicht eines API-Designers hätte ich gerne andere Modifikatoren für Schnittstellenmethoden verwenden können, z final
. Dies ist nützlich, wenn Sie praktische Methoden hinzufügen, um "versehentliche" Überschreibungen bei der Implementierung von Klassen zu verhindern:
interface Sender {
// Convenience method to send an empty message
default final void send() {
send(null);
}
// Implementations should only implement this method
void send(String message);
}
Das Obige ist bereits gängige Praxis, wenn Sender
es sich um eine Klasse handelt:
abstract class Sender {
// Convenience method to send an empty message
final void send() {
send(null);
}
// Implementations should only implement this method
abstract void send(String message);
}
Nun, default
und final
widersprechen offensichtlich Schlüsselwörtern, aber das Standardschlüsselwort selbst wäre nicht unbedingt erforderlich gewesen , daher gehe ich davon aus, dass dieser Widerspruch absichtlich ist, um die subtilen Unterschiede zwischen "Klassenmethoden mit Körper" (nur Methoden) und "Schnittstelle" widerzuspiegeln Methoden mit Körper " (Standardmethoden), dh Unterschiede, die ich noch nicht verstanden habe.
Zu einem bestimmten Zeitpunkt wurde die Unterstützung für Modifikatoren wie static
und final
für Schnittstellenmethoden noch nicht vollständig untersucht, unter Berufung auf Brian Goetz :
Der andere Teil ist, wie weit wir gehen werden, um Tools zum Erstellen von Klassen in Schnittstellen wie endgültigen Methoden, privaten Methoden, geschützten Methoden, statischen Methoden usw. zu unterstützen. Die Antwort lautet: Wir wissen es noch nicht
Seit dieser Zeit Ende 2011 wurde offensichtlich die Unterstützung für static
Methoden in Schnittstellen hinzugefügt. Dies hat den JDK-Bibliotheken selbst, wie z. B. mit, einen großen Mehrwert gebracht Comparator.comparing()
.
Frage:
Was ist der Grund final
(und auch static final
), warum es nie zu Java 8-Schnittstellen gekommen ist?
quelle
final
verhindert , dass eine Methode überschrieben wird, und wenn Sie sehen, wie Sie Methoden überschreiben MÜSSEN, die von Schnittstellen geerbt wurden, sehe ich nicht, warum es sinnvoll wäre, sie endgültig zu machen. Es sei denn, es sollte bedeuten, dass die Methode endgültig ist, nachdem sie einmal überschrieben wurde. In diesem Fall gibt es möglicherweise qas Schwierigkeiten? Wenn ich dieses Recht nicht verstehe, lass mich bitte kmow. Scheint interessantfinal
könnte verhindert werden, dass implementierende Klassen die Standardimplementierung einer Schnittstellenmethode überschreiben.Antworten:
Diese Frage hängt bis zu einem gewissen Grad mit zusammen Was ist der Grund, warum "synchronisiert" in Java 8-Schnittstellenmethoden nicht zulässig ist?
Das Wichtigste, was Sie über Standardmethoden verstehen sollten, ist, dass das primäre Entwurfsziel darin besteht Entwicklung der Benutzeroberfläche ist und nicht "Schnittstellen in (mittelmäßige) Merkmale verwandeln". Obwohl es einige Überschneidungen zwischen den beiden gibt und wir versucht haben, uns an die letzteren anzupassen, wo sie den ersteren nicht im Wege standen, sind diese Fragen am besten zu verstehen, wenn man sie in diesem Licht betrachtet. (Hinweis zu , dass Klassenmethoden sind verschieden von Interface - Methoden zu gehen, egal , was die Absicht, auf Grund der Tatsache , dass Interface - Methoden können mehrfach vererbt werden.)
Die Grundidee einer Standardmethode lautet: Es handelt sich um eine Schnittstellenmethode mit einer Standardimplementierung, und eine abgeleitete Klasse kann eine spezifischere Implementierung bereitstellen. Und da das Design Center die Entwicklung von Schnittstellen war, war es ein kritisches Entwurfsziel, dass Standardmethoden zu Schnittstellen hinzugefügt werden können nach der Tat in einer Quelle-kompatibel und binärkompatibel Weise.
Die zu einfache Antwort auf "Warum nicht endgültige Standardmethoden?" Lautet, dass der Body dann nicht einfach die Standardimplementierung, sondern die einzige Implementierung wäre. Das ist zwar eine etwas zu einfache Antwort, gibt uns aber einen Hinweis darauf, dass die Frage bereits in eine fragwürdige Richtung geht.
Ein weiterer Grund, warum endgültige Schnittstellenmethoden fragwürdig sind, besteht darin, dass sie für Implementierer unmögliche Probleme verursachen. Angenommen, Sie haben:
Hier ist alles gut;
C
erbtfoo()
vonA
. AngenommenB
, es wird einefoo
Methode mit einer Standardeinstellung geändert :Wenn wir jetzt zur Neukompilierung gehen,
C
teilt uns der Compiler mit, dass er nicht weiß, für welches Verhalten er erben sollfoo()
, und dassC
er es überschreiben muss (undA.super.foo()
möglicherweise delegieren kann, wenn er dasselbe Verhalten beibehalten möchte). Aber was ist, wennB
hatte seinen Standard gemachtfinal
undA
steht nicht unter der Kontrolle des Autors vonC
? JetztC
ist unwiederbringlich gebrochen; Es kann nicht kompiliert werden, ohne es zu überschreibenfoo()
, aber es kann nicht überschrieben werdenfoo()
wenn es endgültig warB
.Dies ist nur ein Beispiel, aber der Punkt ist, dass die Endgültigkeit von Methoden wirklich ein Werkzeug ist, das in der Welt der Einzelvererbungsklassen (im Allgemeinen, die den Zustand mit dem Verhalten koppeln) sinnvoller ist als Schnittstellen, die lediglich zum Verhalten beitragen und multipliziert werden können vererbt. Es ist zu schwer zu überlegen, "welche anderen Schnittstellen in den späteren Implementierer eingemischt werden könnten", und das Zulassen, dass eine Schnittstellenmethode endgültig ist, würde diese Probleme wahrscheinlich verursachen (und sie würden nicht die Person in die Luft jagen, die die Schnittstelle geschrieben hat, sondern die armer Benutzer, der versucht, es zu implementieren.)
Ein weiterer Grund, sie nicht zuzulassen, ist, dass sie nicht bedeuten würden, was Sie denken, dass sie bedeuten. Eine Standardimplementierung wird nur berücksichtigt, wenn die Klasse (oder ihre Oberklassen) keine Deklaration (konkret oder abstrakt) der Methode bereitstellt. Wenn eine Standardmethode endgültig wäre, aber eine Oberklasse die Methode bereits implementiert hätte, würde die Standardeinstellung ignoriert, was wahrscheinlich nicht das ist, was der Standardautor erwartet hatte, als er sie für endgültig erklärte. (Dieses Vererbungsverhalten spiegelt das Design Center für Standardmethoden - Schnittstellenentwicklung wider. Es sollte möglich sein, vorhandenen Schnittstellen, die bereits implementiert sind, eine Standardmethode (oder eine Standardimplementierung zu einer vorhandenen Schnittstellenmethode) hinzuzufügen, ohne die zu ändern Verhalten vorhandener Klassen, die die Schnittstelle implementieren,
quelle
In der Lambda-Mailingliste gibt es viele Diskussionen darüber . Eine davon, die eine Menge Diskussionen über all diese Dinge zu enthalten scheint, ist die folgende: Auf Sichtbarkeit der verschiedenen Schnittstellenmethoden (war Final Defenders) .
In dieser Diskussion stellt Talden, der Autor der ursprünglichen Frage , etwas, das Ihrer Frage sehr ähnlich ist:
Schließlich war Brian Goetz 'Antwort :
Daher wurde es höchstwahrscheinlich nie implementiert, da es nie Teil des Geltungsbereichs war. Es wurde nie rechtzeitig vorgeschlagen, darüber nachzudenken.
In einer weiteren hitzigen Diskussion über die Methoden des endgültigen Verteidigers zu diesem Thema sagte Brian erneut :
Dies bestätigt meine Theorie, dass es einfach nicht Teil des Umfangs oder des Entwurfs war. Sie haben genügend Funktionen bereitgestellt, um die Probleme der API-Evolution zu lösen.
quelle
Es wird schwer sein , zu finden und zu identifizieren „ die“ Antwort, für die resons in den Kommentaren von @EJP erwähnt: Es gibt ungefähr 2 (+/- 2) Menschen in der Welt, die der definitive Antwort geben kann überhaupt . Und im Zweifel könnte die Antwort nur so etwas wie "Die Unterstützung der endgültigen Standardmethoden schien die Mühe der Umstrukturierung der internen Anrufauflösungsmechanismen nicht wert zu sein". Dies ist natürlich Spekulation, aber es wird zumindest durch subtile Beweise gestützt, wie diese Aussage (von einer der beiden Personen) in der OpenJDK-Mailingliste :
und triviale Tatsachen wie diese, dass eine Methode einfach nicht als (wirklich) endgültige Methode betrachtet wird, wenn es sich um eine
default
Methode handelt, wie sie derzeit in der Methode :: is_final_method implementiert ist Methode im OpenJDK .Weitere wirklich "autorisierende" Informationen sind in der Tat schwer zu finden, selbst bei übermäßigen Websuchen und durch Lesen von Commit-Protokollen. Ich dachte, dass es mit möglichen Mehrdeutigkeiten während der Auflösung von Schnittstellenmethodenaufrufen mit der
invokeinterface
Anweisung und und Klassenmethodenaufrufen zusammenhängen könnte, die derinvokevirtual
Anweisung entsprechen: Für dieinvokevirtual
Anweisung kann es eine einfache vtable- Suche geben, da die Methode entweder vererbt werden muss aus einer Oberklasse oder direkt von der Klasse implementiert. Im Gegensatz dazu muss eininvokeinterface
Aufruf die jeweilige Anrufstelle untersuchen, um herauszufinden, auf welche Schnittstelle sich dieser Aufruf tatsächlich bezieht (dies wird ausführlicher in den erläutert Seite InterfaceCalls des HotSpot-Wikis ). Jedoch,final
Methoden werden entweder überhaupt nicht in die vtable eingefügt oder ersetzen vorhandene Einträge in der vtable (siehe klassVtable.cpp. Zeile 333 ), und in ähnlicher Weise ersetzen Standardmethoden vorhandene Einträge in der vtable (siehe klassVtable.cpp, Zeile 202 ). . Der eigentliche Grund (und damit die Antwort) muss also tiefer in den (ziemlich komplexen) Methodenauflösungsmechanismen verborgen sein, aber vielleicht werden diese Referenzen dennoch als hilfreich angesehen, sei es nur für andere, die es schaffen, die tatsächliche Antwort abzuleiten davon.quelle
int
s gibt, aber siehe stackoverflow.com/questions/430346 ... ...synchronized
aufdefault
Methoden.final
Frage, und es ist etwas demütig, dass niemand an ähnliche Beispiele zu denken schien (oder vielleicht einige über diese Beispiele nachdachten, sich aber nicht autoritativ genug fühlten, um zu antworten). Nun wird das letzte Wort gesprochen (Entschuldigung, ich muss das tun :).Ich würde nicht denken, dass es notwendig ist,
final
eine Convenience-Interface-Methode anzugeben. Ich kann zwar zustimmen, dass dies hilfreich sein mag , aber anscheinend haben die Kosten die Vorteile übergewichtet .Was Sie in beiden Fällen tun sollen, ist, das richtige Javadoc für die Standardmethode zu schreiben und genau zu zeigen, was die Methode ist und was nicht. Auf diese Weise dürfen die Klassen, die die Schnittstelle implementieren, die Implementierung nicht ändern, obwohl es keine Garantien gibt.
Jeder kann eine schreiben
Collection
, die an der Benutzeroberfläche haftet und dann Dinge in Methoden tut, die absolut kontraintuitiv sind. Es gibt keine Möglichkeit, sich davor zu schützen, außer umfangreiche Unit-Tests zu schreiben.quelle
final
entschieden wurde, Java 8-interface
Methoden nicht zuzulassen. Ein unzureichendes Kosten-Nutzen-Verhältnis ist ein guter Kandidat, aber bisher ist das Spekulation.Wir fügen
default
unserer Methode ein Schlüsselwort hinzu,interface
wenn wir wissen, dass die Klasse die Implementierung erweitertinterface
oder nichtoverride
. Was aber, wenn wir eine Methode hinzufügen möchten, die von keiner implementierenden Klasse überschrieben werden soll? Nun, wir hatten zwei Möglichkeiten:default
final
Methode hinzu.static
Methode hinzu.Java sagt nun, dass wir eine
class
Implementierung dieser Methode in unserer Klasse bereitstellen müssen , wenn wir zwei oder mehr implementieren,interfaces
sodass sie einedefault
Methode mit genau demselben Methodennamen und derselben Signatur haben, dh sie sind doppelt vorhanden. Im Falle vondefault
final
Methoden können wir keine Implementierung bereitstellen und stecken fest. Und deshalb wird dasfinal
Schlüsselwort in Schnittstellen nicht verwendet.quelle