Beim Durchsuchen des Java Collections Framework ist mir aufgefallen, dass einige der Schnittstellen den Kommentar enthalten (optional operation)
. Diese Methoden ermöglichen das Implementieren von Klassen über eine, UnsupportedOperationException
wenn sie diese Methode einfach nicht implementieren möchten.
Ein Beispiel hierfür ist die addAll
Methode in der Set Interface
.
Nun, wie in dieser Reihe von Fragen dargelegt, sind Schnittstellen ein bestimmender Vertrag für das, was die Nutzung erwarten kann.
Schnittstellen sind wichtig, weil sie unterscheiden, was eine Klasse tut und wie sie es tut. Der Vertrag, der festlegt, was ein Kunde erwarten kann, lässt dem Entwickler die Möglichkeit, ihn nach Belieben umzusetzen, solange er den Vertrag einhält.
und
Eine Schnittstelle ist eine Beschreibung der Aktionen, die ein Objekt ausführen kann. Wenn Sie beispielsweise einen Lichtschalter betätigen, leuchtet das Licht, und es ist Ihnen egal, wie. In der objektorientierten Programmierung ist eine Schnittstelle eine Beschreibung aller Funktionen, die ein Objekt haben muss, um ein "X" zu sein.
und
Ich denke, der schnittstellenbasierte Ansatz ist bedeutend besser. Sie können dann Ihre Abhängigkeiten gut verspotten, und alles ist im Grunde genommen weniger eng miteinander verbunden.
Was ist der Sinn einer Schnittstelle?
Interface + Extension (Mixin) vs Basisklasse
Angesichts der Tatsache, dass der Zweck von Schnittstellen darin besteht, einen Vertrag zu definieren und Ihre Abhängigkeiten lose miteinander zu verknüpfen, wird UnsupportedOperationException
der Zweck nicht durch einige Methoden zunichte gemacht ? Es bedeutet, dass ich nicht mehr weitergegeben werden kann Set
und nur noch benutze addAll
. Vielmehr muss ich wissen, welche Implementierung von Set
mir bestanden wurde, damit ich wissen kann, ob ich verwenden kann addAll
oder nicht. Das erscheint mir ziemlich wertlos.
Worum geht es also UnsupportedOperationException
? Gleicht es nur den alten Code aus und sie müssen ihre Schnittstellen bereinigen? Oder hat es einen sinnlicheren Zweck, den ich vermisse?
quelle
addAll
inHashSet
. Es verschiebt sich zu der Standardimplementierung, inAbstractCollection
der ganz sicher nicht geworfen wirdUnsupportedOperationException
.src.zip
funktioniert sie hervorragend. Es ist hilfreich, genau zu wissen, welchen Code die JRE manchmal ausführt, und sich nicht auf JavaDoc zu beschränken, was etwas ausführlich sein kann.Antworten:
Schauen Sie sich die folgenden Schnittstellen an:
Diese Schnittstellen deklarieren alle Mutationsmethoden als optional. Dies dokumentiert implizit die Tatsache, dass die Collections- Klasse Implementierungen dieser unveränderlichen Schnittstellen zurückgeben kann, dh, dass diese optionalen Mutationsoperationen garantiert fehlschlagen. Gemäß dem Vertrag in JavaDoc müssen jedoch alle Implementierungen dieser Schnittstellen die Leseoperationen zulassen. Dies beinhaltet die "normalen" Implementierungen wie
HashSet
undLinkedList
sowie die unveränderlichen Wrapper inCollections
.Kontrast zu den Queue-Interfaces:
Diese Schnittstelle kann nicht angeben , alle optionalen Operationen: eine Warteschlange, per Definition, wird entwickelt , um Angebot und Umfrage - Elemente in einer FIFO - Weise. Eine unveränderliche Warteschlange ist ungefähr so nützlich wie ein Auto ohne Räder.
Eine häufige Idee, die immer wieder auftaucht, ist eine Vererbungshierarchie, die sowohl veränderbare als auch unveränderliche Objekte enthält. Diese haben jedoch alle Nachteile. Die Komplexität trübt das Wasser, ohne das Problem tatsächlich zu lösen.
Eine Hypothese
Set
könnte die Leseoperationen haben, und eine UnterschnittstelleMutableSet
könnte die Schreiboperationen haben. Liskov sagt uns, dass aMutableSet
dann an alles übergeben werden könnte, was a benötigtSet
. Das hört sich zunächst in Ordnung an, aber denken Sie daran, dass eine Methode, die davon ausgeht, dass die zugrunde liegende Menge nicht geändert wird, während des Lesens: Es wäre möglich, dass zwei Threads dieselbe Menge verwenden und die Invariante der Menge verletzen, die sich nicht ändert. Dies kann zu Problemen führen, z. B. wenn eine Methode ein Element zweimal aus der Menge liest und es beim ersten Mal vorhanden ist, beim zweiten Mal jedoch nicht.Set
könnte keine direkten Implementierungen haben, stattdessenMutableSet
undImmutableSet
als Subschnittstellen, die dann zum Implementieren von Klassen verwendet werden. Dies hat das gleiche Problem wie oben: An einem bestimmten Punkt in der Hierarchie weist eine Schnittstelle widersprüchliche Invarianten auf. Einer sagt "Diese Menge muss veränderlich sein" und der andere sagt "Diese Menge kann sich nicht ändern".Es könnte zwei völlig getrennte Hierarchien für veränderbare und unveränderliche Datenstrukturen geben. Dies fügt eine Tonne zusätzlicher Komplexität hinzu, was letztendlich nur ein sehr geringer Gewinn ist. Dies hat auch die spezifische Schwäche von Methoden, die sich nicht um die Veränderbarkeit kümmern (z. B. ich möchte nur eine Liste iterieren), müssen nun zwei separate Schnittstellen unterstützen. Da Java statisch typisiert ist, bedeutet dies zusätzliche Methoden zur Behandlung beider Schnittstellenhierarchien.
Wir könnten eine einzige Schnittstelle haben und Implementierungen erlauben, Ausnahmen auszulösen, wenn eine Methode darauf nicht anwendbar ist. Dies ist der Weg, den Java eingeschlagen hat und der am sinnvollsten ist. Die Anzahl der Schnittstellen ist auf ein Minimum beschränkt, und es gibt keine Invarianten für die Wandlungsfähigkeit, da die dokumentierte Schnittstelle in keiner Weise die Wandlungsfähigkeit garantiert . Wenn eine Unveränderlichkeitsinvariante erforderlich ist, verwenden Sie die Wrapper in
Collections
. Wenn eine Methode eine Sammlung nicht ändern muss, ändern Sie sie einfach nicht. Der Nachteil ist, dass eine Methode nicht garantieren kann, dass sich eine Auflistung in einem anderen Thread nicht ändert, wenn sie von außen bereitgestellt wird. Dies ist jedoch ein Problem der aufrufenden Methode (oder ihrer aufrufenden Methode).Verwandte Lektüre: Warum enthält Java 8 keine unveränderlichen Sammlungen?
quelle
MutableCollection
?Es ist im Grunde YAGNI. Alle konkreten Sammlungen in der Standardbibliothek sind veränderbar und implementieren oder erben die optionalen Operationen. Sie interessieren sich nicht für unveränderliche Allzweck-Sammlungen und die große Mehrheit der Java-Entwickler auch nicht. Sie werden keine vollständige Schnittstellenhierarchie nur für unveränderliche Sammlungen erstellen und dann keine Implementierungen einschließen.
Auf der anderen Seite gibt es einige spezielle Werte oder "virtuelle" Sammlungen, die als unveränderlich sehr nützlich sein könnten, wie beispielsweise leere Mengen und nCopies . Es gibt auch unveränderliche Sammlungen von Drittanbietern (wie Scala's), die möglicherweise vorhandenen Java-Code aufrufen möchten, sodass die Möglichkeit für unveränderliche Sammlungen auf die am wenigsten störende Weise offen bleibt.
quelle