Java 8 Standardmethoden als Merkmale: sicher?


116

Ist es eine sichere Praxis, Standardmethoden als Version von Merkmalen für Arme in Java 8 zu verwenden?

Einige behaupten, es könnte Pandas traurig machen, wenn man sie nur deswegen benutzt, weil es cool ist, aber das ist nicht meine Absicht. Es wird auch oft daran erinnert, dass Standardmethoden eingeführt wurden, um die API-Entwicklung und die Abwärtskompatibilität zu unterstützen, was wahr ist, aber dies macht es nicht falsch oder verdreht, sie als Merkmale an sich zu verwenden.

Ich denke an folgenden praktischen Anwendungsfall :

public interface Loggable {
    default Logger logger() {
        return LoggerFactory.getLogger(this.getClass());
    }
}

Oder definieren Sie vielleicht ein PeriodTrait:

public interface PeriodeTrait {
    Date getStartDate();
    Date getEndDate();
    default isValid(Date atDate) {
        ...
    }
}

Zugegeben, Komposition könnte verwendet werden (oder sogar Hilfsklassen), aber sie scheint ausführlicher und unübersichtlicher zu sein und erlaubt es nicht, vom Polymorphismus zu profitieren.

Ist es also in Ordnung / sicher, Standardmethoden als Grundmerkmale zu verwenden , oder sollte ich mir Sorgen über unvorhergesehene Nebenwirkungen machen?

Mehrere Fragen zu SO beziehen sich auf Java- und Scala-Merkmale. Darum geht es hier nicht. Ich frage auch nicht nur nach Meinungen. Stattdessen suche ich nach einer maßgeblichen Antwort oder zumindest nach Erkenntnissen vor Ort: Wenn Sie Standardmethoden als Merkmale für Ihr Unternehmensprojekt verwendet haben, hat sich herausgestellt, dass es sich um eine Zeitbombe handelt?


Es scheint mir, dass Sie die gleichen Vorteile aus dem Erben einer abstrakten Klasse ziehen können und sich nicht darum kümmern müssen, Pandas zum Weinen zu bringen ... Der einzige Grund, warum ich eine Standardmethode in einer Schnittstelle verwenden kann, ist, dass Sie die Funktionalität benötigen und nicht können Ändern Sie eine Reihe von Legacy-Code, der auf der Schnittstelle basiert.
Deven Phillips

1
Ich stimme @ infosec812 hinsichtlich der Erweiterung einer abstrakten Klasse zu, die ein eigenes statisches Loggerfeld definiert. Würde Ihre logger () -Methode nicht jedes Mal, wenn sie aufgerufen wird, eine neue Logger-Instanz instanziieren?
Eidan Spiegel

Für die Protokollierung sollten Sie sich Projectlombok.org und deren @ Slf4j-Annotation ansehen.
Deven Phillips

Antworten:


120

Die kurze Antwort lautet: Es ist sicher, wenn Sie sie sicher verwenden :)

Die snarky Antwort: Sag mir, was du mit Eigenschaften meinst, und vielleicht gebe ich dir eine bessere Antwort :)

In aller Ernsthaftigkeit ist der Begriff "Merkmal" nicht genau definiert. Viele Java-Entwickler sind mit Merkmalen am besten vertraut, da sie in Scala ausgedrückt werden. Scala ist jedoch weit davon entfernt, die erste Sprache zu sein, die Merkmale aufweist, entweder im Namen oder in der Wirkung.

In Scala sind Merkmale beispielsweise zustandsbehaftet (können varVariablen enthalten). In der Festung sind sie reines Verhalten. Javas Schnittstellen zu Standardmethoden sind zustandslos. Bedeutet das, dass sie keine Eigenschaften sind? (Hinweis: Das war eine Trickfrage.)

Auch in Scala werden Merkmale durch Linearisierung zusammengesetzt. Wenn die Klasse AMerkmale Xund erweitert Y, bestimmt die Reihenfolge, in der Xund Ygemischt werden, wie Konflikte zwischen Xund Ygelöst werden. In Java ist dieser Linearisierungsmechanismus nicht vorhanden (er wurde teilweise abgelehnt, weil er zu "un-Java-ähnlich" war.)

Der Hauptgrund für das Hinzufügen von Standardmethoden zu Schnittstellen war die Unterstützung der Schnittstellenentwicklung , aber wir waren uns bewusst, dass wir darüber hinausgehen. Ob Sie dies als "Interface Evolution ++" oder "Traits--" betrachten, ist eine Frage der persönlichen Interpretation. Um Ihre Frage zur Sicherheit zu beantworten ... Solange Sie sich an das halten, was der Mechanismus tatsächlich unterstützt, anstatt zu versuchen, ihn auf etwas zu dehnen, das er nicht unterstützt, sollte es Ihnen gut gehen.

Ein wichtiges Entwurfsziel war, dass Standardmethoden aus Sicht des Clients einer Schnittstelle nicht von "normalen" Schnittstellenmethoden zu unterscheiden sind. Die Standardeinstellung einer Methode ist daher nur für den Designer und Implementierer der Schnittstelle interessant .

Hier sind einige Anwendungsfälle, die innerhalb der Entwurfsziele liegen:

  • Schnittstellenentwicklung. Hier fügen wir einer vorhandenen Schnittstelle eine neue Methode hinzu, die eine sinnvolle Standardimplementierung in Bezug auf vorhandene Methoden auf dieser Schnittstelle aufweist. Ein Beispiel wäre das Hinzufügen der forEachMethode zu Collection, wobei die Standardimplementierung in Bezug auf die iterator()Methode geschrieben wird.

  • "Optionale" Methoden. Hier sagt der Designer einer Schnittstelle: "Implementierer müssen diese Methode nicht implementieren, wenn sie bereit sind, mit den damit verbundenen Einschränkungen der Funktionalität zu leben." Zum Beispiel Iterator.removewurde eine Standardeinstellung gegeben, die wirft UnsupportedOperationException; Da die überwiegende Mehrheit der Implementierungen Iteratordieses Verhaltens ohnehin aufweist, ist diese Methode standardmäßig im Wesentlichen optional. (Wenn das Verhalten von AbstractCollectionals Standardwert für ausgedrückt würde Collection, könnten wir dasselbe für die mutativen Methoden tun.)

  • Convenience-Methoden. Dies sind Methoden, die nur der Einfachheit halber dienen und wiederum im Allgemeinen in Form von nicht standardmäßigen Methoden für die Klasse implementiert werden. Die logger()Methode in Ihrem ersten Beispiel ist ein vernünftiges Beispiel dafür.

  • Kombinatoren. Hierbei handelt es sich um Kompositionsmethoden, mit denen neue Instanzen der Schnittstelle basierend auf der aktuellen Instanz instanziiert werden. Zum Beispiel sind die Methoden Predicate.and()oder Comparator.thenComparing()Beispiele für Kombinatoren.

Wenn Sie eine Standardimplementierung bereitstellen, sollten Sie auch eine Spezifikation für die Standardimplementierung angeben (im JDK verwenden wir hierfür das @implSpecJavadoc-Tag), damit Implementierer besser verstehen, ob sie die Methode überschreiben möchten oder nicht. Einige Standardeinstellungen, wie Convenience-Methoden und Kombinatoren, werden fast nie überschrieben. andere, wie optionale Methoden, werden häufig überschrieben. Sie müssen genügend Spezifikationen (nicht nur Dokumentation) darüber bereitstellen, was die Standardeinstellung verspricht, damit der Implementierer eine vernünftige Entscheidung darüber treffen kann, ob er sie überschreiben muss.


9
Vielen Dank, Brian, für diese umfassende Antwort. Jetzt kann ich Standardmethoden mit leichtem Herzen verwenden. Leser: Weitere Informationen von Brian Goetz zur Schnittstellenentwicklung und zu Standardmethoden finden Sie beispielsweise in NightHacking Worldwide Lambdas .
Youri

Danke @ brian-goetz. Nach dem, was Sie gesagt haben, sind Javas Standardmethoden näher an der Vorstellung traditioneller Merkmale, wie sie im Artikel von Ducasse et al. ( Scg.unibe.ch/archive/papers/Duca06bTOPLASTraits.pdf ) definiert sind. Scala "Merkmale" scheinen mir überhaupt keine Merkmale zu sein, da sie einen Zustand haben, Linearisierung in ihrer Zusammensetzung verwenden und es scheint, dass sie auch implizit Methodenkonflikte lösen - und dies sind alles Dinge, die traditionelle Merkmale nicht tun haben. Tatsächlich würde ich sagen, dass Scala-Merkmale eher Mixins als Eigenschaften sind. Was denken Sie? PS: Ich habe noch nie in Scala codiert.
Adino

Wie wäre es mit der Verwendung leerer Standardimplementierungen für Methoden in einer Schnittstelle, die als Listener fungiert? Die Listener-Implementierung ist möglicherweise nur daran interessiert, auf einige Schnittstellenmethoden zu warten. Wenn Sie also Methoden als Standard festlegen, muss der Implementierer nur die Methoden implementieren, die zum Abhören erforderlich sind.
Lahiru Chandima

1
@ LahiruChandima Wie bei MouseListener? Soweit dieser API-Stil sinnvoll ist, passt er in den Bucket "Optionale Methoden". Stellen Sie sicher, dass Sie die Option klar dokumentieren!
Brian Goetz

Ja. Genau wie in MouseListener. Danke für die Antwort.
Lahiru Chandima
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.