Standardmethoden sind ein schönes neues Tool in unserer Java-Toolbox. Ich habe jedoch versucht, eine Schnittstelle zu schreiben, die eine default
Version der toString
Methode definiert. Java sagt mir, dass dies verboten ist, da in deklarierte Methoden java.lang.Object
möglicherweise nicht default
bearbeitet werden. Warum ist das so?
Ich weiß, dass es die Regel "Basisklasse gewinnt immer" gibt, daher würde standardmäßig (pun;) jede default
Implementierung einer Object
Methode von der Methode Object
sowieso überschrieben . Ich sehe jedoch keinen Grund, warum es keine Ausnahme für Methoden aus Object
der Spezifikation geben sollte. Insbesondere kann toString
es sehr nützlich sein, eine Standardimplementierung zu haben.
Was ist der Grund, warum Java-Designer beschlossen haben, default
Methoden, die Methoden überschreiben , nicht zuzulassen Object
?
quelle
Antworten:
Dies ist ein weiteres Problem des Sprachdesigns, das "offensichtlich eine gute Idee" zu sein scheint, bis Sie anfangen zu graben und feststellen, dass es tatsächlich eine schlechte Idee ist.
Diese Mail hat viel zu diesem Thema (und auch zu anderen Themen). Es gab mehrere Designkräfte, die zusammen kamen, um uns zum aktuellen Design zu bringen:
AbstractList
in eine Schnittstelle verwandeln ), feststellen, dass das Erben von equals / hashCode / toString stark an die einzelne Vererbung und den Status gebunden ist und die Schnittstellen mehrfach vererbt und zustandslos sind.Sie haben bereits das Ziel "Einfach halten" angesprochen. Die Vererbungs- und Konfliktlösungsregeln sind sehr einfach gestaltet (Klassen gewinnen über Schnittstellen, abgeleitete Schnittstellen gewinnen über Superschnittstellen und alle anderen Konflikte werden von der implementierenden Klasse gelöst.) Natürlich könnten diese Regeln angepasst werden, um eine Ausnahme zu machen, aber Ich denke, Sie werden feststellen, wenn Sie an dieser Schnur ziehen, dass die inkrementelle Komplexität nicht so gering ist, wie Sie vielleicht denken.
Natürlich gibt es einen gewissen Nutzen, der mehr Komplexität rechtfertigen würde, aber in diesem Fall ist er nicht vorhanden. Die Methoden, über die wir hier sprechen, sind equals, hashCode und toString. Bei diesen Methoden geht es ausschließlich um den Objektstatus, und es ist die Klasse, die den Status besitzt, nicht die Schnittstelle, die am besten bestimmen kann, was Gleichheit für diese Klasse bedeutet (insbesondere, da der Vertrag für Gleichheit ziemlich stark ist; siehe Effektiv) Java für einige überraschende Konsequenzen); Interface-Writer sind einfach zu weit entfernt.
Es ist einfach, das
AbstractList
Beispiel herauszuholen . Es wäre schön, wenn wirAbstractList
das Verhalten loswerden und in dieList
Benutzeroberfläche einfügen könnten . Wenn Sie jedoch über dieses offensichtliche Beispiel hinausgehen, gibt es nicht viele andere gute Beispiele. An der WurzelAbstractList
ist für die Einzelvererbung ausgelegt. Schnittstellen müssen jedoch für Mehrfachvererbung ausgelegt sein.Stellen Sie sich außerdem vor, Sie schreiben diese Klasse:
Der
Foo
Autor betrachtet die Supertypen, sieht keine Implementierung von Gleichheit und kommt zu dem Schluss, dass er nur Gleichheit von erben muss, um Referenzgleichheit zu erhaltenObject
. Nächste Woche fügt der Bibliotheksbetreuer für Bar "hilfreich" eine Standardimplementierung hinzuequals
. Ups! Jetzt wurde die Semantik vonFoo
durch eine Schnittstelle in einer anderen Wartungsdomäne "hilfreich" unterbrochen, indem ein Standard für eine allgemeine Methode hinzugefügt wurde.Standardeinstellungen sollen Standardeinstellungen sein. Das Hinzufügen einer Standardeinstellung zu einer Schnittstelle, an der keine vorhanden war (irgendwo in der Hierarchie), sollte die Semantik konkreter Implementierungsklassen nicht beeinflussen. Aber wenn Standardeinstellungen Objektmethoden "überschreiben" könnten, wäre das nicht wahr.
Obwohl es als harmloses Feature erscheint, ist es in der Tat ziemlich schädlich: Es erhöht die Komplexität bei geringer inkrementeller Ausdruckskraft und macht es viel zu einfach, dass gut gemeinte, harmlos aussehende Änderungen an separat kompilierten Schnittstellen untergraben werden die beabsichtigte Semantik der Implementierung von Klassen.
quelle
hashCode
und gefährlich wäreequals
, aber ich denke, es wäre sehr nützlich fürtoString
. Zum Beispiel, einigeDisplayable
könnten Schnittstelle eine DefinitionString display()
Methode, und es würde eine Tonne vorformulierten speichern zu können , definieren ,default String toString() { return display(); }
inDisplayable
, anstatt jeden einzelnen zu erfordernDisplayable
implementierentoString()
eine oder zu verlängernDisplayableToString
Basisklasse.toString()
es nur auf den Schnittstellenmethoden basiert, können Sie einfach etwas Ähnlichesdefault String toStringImpl()
zur Schnittstelle hinzufügen undtoString()
in jeder Unterklasse überschreiben , um die Schnittstellenimplementierung aufzurufen - ein bisschen hässlich, aber funktioniert und besser als nichts. :) Eine andere Möglichkeit , es zu tun ist , wie etwas zu machenObjects.hash()
,Arrays.deepEquals()
undArrays.deepToString()
. + 1'd für die Antwort von @ BrianGoetz!default toString()
Override in einer funktionalen Schnittstelle würde es uns zumindest ermöglichen, die Signatur der Funktion und die übergeordnete Klasse des Implementierers auszuspucken. Noch besser, wenn wir einige rekursive Strings-Strategien zum Tragen bringen könnten, könnten wir durch den Abschluss gehen, um eine wirklich gute Beschreibung des Lambda zu erhalten und so die Lambda-Lernkurve drastisch zu verbessern.Es ist verboten, Standardmethoden in Schnittstellen für Methoden in zu definieren
java.lang.Object
, da die Standardmethoden niemals "erreichbar" wären.Standardschnittstellenmethoden können in Klassen, die die Schnittstelle implementieren, überschrieben werden, und die Klassenimplementierung der Methode hat eine höhere Priorität als die Schnittstellenimplementierung, selbst wenn die Methode in einer Oberklasse implementiert ist. Da alle Klassen von erben
java.lang.Object
, haben die Methoden injava.lang.Object
Vorrang vor der Standardmethode in der Schnittstelle und werden stattdessen aufgerufen.Brian Goetz von Oracle bietet in diesem Mailinglistenbeitrag einige weitere Details zur Entwurfsentscheidung .
quelle
Ich sehe nicht in den Kopf von Java-Sprachautoren, also können wir nur raten. Aber ich sehe viele Gründe und stimme ihnen in dieser Ausgabe absolut zu.
Der Hauptgrund für die Einführung von Standardmethoden besteht darin, Schnittstellen neue Methoden hinzufügen zu können, ohne die Abwärtskompatibilität älterer Implementierungen zu beeinträchtigen. Die Standardmethoden können auch verwendet werden, um "Convenience" -Methoden bereitzustellen, ohne dass sie in jeder der implementierenden Klassen definiert werden müssen.
Nichts davon gilt für String und andere Objektmethoden. Einfach ausgedrückt, wurden Standardmethoden entwickelt, um das Standardverhalten bereitzustellen, wenn es keine andere Definition gibt. Keine Implementierungen bereitzustellen, die mit anderen vorhandenen Implementierungen "konkurrieren".
Die Regel "Basisklasse gewinnt immer" hat auch ihre soliden Gründe. Es wird angenommen, dass Klassen reale Implementierungen definieren , während Schnittstellen Standardimplementierungen definieren , die etwas schwächer sind.
Die Einführung von Ausnahmen zu allgemeinen Regeln führt zu unnötiger Komplexität und wirft andere Fragen auf. Das Objekt ist (mehr oder weniger) eine Klasse wie jedes andere. Warum sollte es sich also anders verhalten?
Alles in allem würde die von Ihnen vorgeschlagene Lösung wahrscheinlich mehr Nachteile als Vorteile bringen.
quelle
Die Argumentation ist sehr einfach, da Object die Basisklasse für alle Java-Klassen ist. Selbst wenn die Methode von Object in einer Schnittstelle als Standardmethode definiert ist, ist sie nutzlos, da die Methode von Object immer verwendet wird. Aus diesem Grund können wir keine Standardmethoden verwenden, die Objektklassenmethoden überschreiben, um Verwirrung zu vermeiden.
quelle
Um eine sehr pedantische Antwort zu geben, ist es nur verboten, eine
default
Methode für eine öffentliche Methode aus zu definierenjava.lang.Object
. Es sind 11 Methoden zu berücksichtigen, die auf drei Arten kategorisiert werden können, um diese Frage zu beantworten.Object
Methoden können nicht habendefault
Methoden , weil sie sindfinal
und können überhaupt nicht außer Kraft gesetzt werden:getClass()
,notify()
,notifyAll()
,wait()
,wait(long)
, undwait(long, int)
.Object
Verfahren haben nichtdefault
Methoden aus den genannten Gründen oben von Brian Goetz:equals(Object)
,hashCode()
, undtoString()
.Zwei der
Object
Methoden können habendefault
Methoden, obwohl der Wert derartigen Ausfall bestenfalls fragwürdig ist:clone()
undfinalize()
.quelle