Java8:Warum ist es verboten, eine Standardmethode für eine Methode aus Java zu definieren.lang.Objekt

StackOverflow https://stackoverflow.com//questions/24016962

Frage

Standardmethoden sind ein nettes neues Werkzeug in unserer Java-Toolbox.Ich habe jedoch versucht, eine Schnittstelle zu schreiben, die a definiert default version des toString Methode.Java sagt mir, dass dies verboten ist, da Methoden deklariert sind in java.lang.Object darf nicht sein defaultaufl.Warum ist das so?

Ich weiß, dass es die Regel "Basisklasse gewinnt immer" gibt, also standardmäßig (Wortspiel;) jede default implementierung eines Object methode würde durch die Methode von überschrieben Object Wie dem auch sei.Ich sehe jedoch keinen Grund, warum es keine Ausnahme für Methoden von geben sollte Object in der Spez.Speziell für toString es kann sehr nützlich sein, eine Standardimplementierung zu haben.

Also, was ist der Grund, warum Java-Designer beschlossen haben, nicht zuzulassen default methoden überschreiben Methoden aus Object?

War es hilfreich?

Lösung

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 zusammenliefen, um uns zum aktuellen Design zu bringen:

  • Der Wunsch, das Vererbungsmodell einfach zu halten;
  • Die Tatsache, dass Sie, sobald Sie über die offensichtlichen Beispiele (z. B. Drehen) hinausschauen AbstractList in eine Schnittstelle), stellen Sie fest, dass das Erben von equals / hashCode / toString stark an einzelne Vererbung und Status gebunden ist und Schnittstellen mehrfach vererbt und zustandslos sind;
  • Dass es möglicherweise die Tür zu einigen überraschenden Verhaltensweisen geöffnet hat.

Sie haben das Ziel "Keep it simple" bereits angesprochen;die Vererbungs- und Konfliktlösungsregeln sind sehr einfach gestaltet (Klassen gewinnen Schnittstellen, abgeleitete Schnittstellen gewinnen 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 anfangen, an dieser Schnur zu 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 da.Die Methoden, über die wir hier sprechen, sind equals , hashCode und toString .Bei diesen Methoden geht es alle von Natur aus um den Objektzustand, und es ist die Klasse, die den Zustand besitzt, nicht die Schnittstelle, die am besten bestimmen kann, was Gleichheit für diese Klasse bedeutet (zumal der Gleichheitsvertrag ziemlich stark ist;siehe Effektives Java für einige überraschende Konsequenzen );Schnittstellenschreiber sind einfach zu weit entfernt.

Es ist einfach, das herauszuziehen AbstractList Beispiel;es wäre schön, wenn wir loswerden könnten AbstractList und das Verhalten in die List Schnittstelle.Aber sobald Sie über dieses offensichtliche Beispiel hinausgehen, gibt es nicht mehr viele andere gute Beispiele.An der Wurzel, AbstractList ist für die einfache Vererbung konzipiert.Schnittstellen müssen jedoch für Mehrfachvererbung ausgelegt sein.

Stellen Sie sich außerdem vor, Sie schreiben diese Klasse:

class Foo implements com.libraryA.Bar, com.libraryB.Moo { 
    // Implementation of Foo, that does NOT override equals
}

Der Foo writer betrachtet die Supertypen, sieht keine Implementierung von equals und kommt zu dem Schluss, dass er nur equals erben muss, um Referenzgleichheit zu erhalten Object.Dann, nächste Woche, fügt der Bibliotheksbetreuer für Bar "hilfreich" einen Standard hinzu equals Umsetzung.Ups!Nun ist die Semantik von Foo wurden von einer Schnittstelle in einer anderen Wartungsdomäne "hilfreich" unterbrochen, indem eine Standardeinstellung für eine gängige Methode hinzugefügt wurde.

Standardwerte sollen Standardwerte sein.Das Hinzufügen eines Defaults zu einer Schnittstelle, an der es keine gab (irgendwo in der Hierarchie), sollte die Semantik konkreter implementierender Klassen nicht beeinflussen.Aber wenn Standardwerte Objektmethoden "überschreiben" könnten, wäre das nicht wahr.

Also, während es wie eine harmlose Eigenschaft scheint, ist es in der Tat ziemlich schädlich:es fügt viel Komplexität für wenig inkrementelle Expressivität hinzu und macht es gut gemeinten, harmlos aussehenden Änderungen an separat kompilierten Schnittstellen viel zu einfach, die beabsichtigte Semantik der Implementierung von Klassen zu untergraben.

Andere Tipps

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 überschrieben werden, die die Schnittstelle implementieren, und die Klassenimplementierung der Methode hat eine höhere Priorität als die Schnittstellenimplementierung, auch wenn die Methode in einer Oberklasse implementiert ist.Da alle Klassen erben von java.lang.Object, die Methoden in java.lang.Object hätte Vorrang vor der Standardmethode in der Schnittstelle und würde stattdessen aufgerufen.

Brian Goetz von Oracle liefert in diesem einige weitere Details zur Designentscheidung mailinglisten-Beitrag.

Ich sehe nicht in den Kopf der Java-Sprachautoren, also können wir nur raten.Aber ich sehe viele Gründe und stimme ihnen in dieser Frage 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 "Komfortmethoden" bereitzustellen, ohne dass sie in jeder der implementierenden Klassen definiert werden müssen.

Nichts davon gilt für toString und andere Methoden von Object .Einfach ausgedrückt, Standardmethoden wurden entwickelt, um die bereitzustellen Standard verhalten, bei dem es keine andere Definition gibt.Keine Implementierungen bereitzustellen, die mit anderen vorhandenen Implementierungen "konkurrieren".

Die Regel "Basisklasse gewinnt immer" hat auch ihre guten Gründe.Es wird angenommen, dass Klassen definieren Real implementierungen, während Schnittstellen definieren Standard implementierungen, die etwas schwächer sind.

Außerdem führt die Einführung von Ausnahmen von allgemeinen Regeln zu unnötiger Komplexität und wirft andere Fragen auf.Object ist (mehr oder weniger) eine Klasse wie jede andere, also warum sollte es ein anderes Verhalten haben?

Alles in allem würde die von Ihnen vorgeschlagene Lösung wahrscheinlich mehr Nachteile als Vorteile mit sich bringen.

Die Argumentation ist sehr einfach, weil Object die Basisklasse für alle Java-Klassen ist.Selbst wenn wir die Methode des Objekts in einer Schnittstelle als Standardmethode definiert haben, ist sie nutzlos, da die Methode des Objekts immer verwendet wird.Aus diesem Grund können wir, um Verwirrung zu vermeiden, keine Standardmethoden haben, die Objektklassenmethoden überschreiben.

Um eine sehr pedantische Antwort zu geben, ist es nur verboten, eine zu definieren default verfahren für eine öffentlich methode von java.lang.Object.Es sind 11 Methoden zu berücksichtigen, die auf drei Arten kategorisiert werden können, um diese Frage zu beantworten.

  1. Sechs der Object methoden können nicht haben default methoden, weil sie sind final und kann überhaupt nicht außer Kraft gesetzt werden: getClass(), notify(), notifyAll(), wait(), wait(long), und wait(long, int).
  2. Drei der Object methoden können nicht haben default methoden aus den oben genannten Gründen von Brian Goetz: equals(Object), hashCode(), und toString().
  3. Zwei der Object Methoden können haben default methoden, obwohl der Wert solcher Standardwerte bestenfalls fraglich ist: clone() und finalize().

    public class Main {
        public static void main(String... args) {
            new FOO().clone();
            new FOO().finalize();
        }
    
        interface ClonerFinalizer {
            default Object clone() {System.out.println("default clone"); return this;}
            default void finalize() {System.out.println("default finalize");}
        }
    
        static class FOO implements ClonerFinalizer {
            @Override
            public Object clone() {
                return ClonerFinalizer.super.clone();
            }
            @Override
            public void finalize() {
                ClonerFinalizer.super.finalize();
            }
        }
    }
    
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top