Gibt es einen guten Grund, warum die Collections.list () -Methode im java.utils
Paket ein ArrayList<T>
anstelle von zurückgibt List<T>
?
Natürlich ArrayList
ist ein a List
, aber ich habe den Eindruck, dass es im Allgemeinen eine gute Praxis ist, den Schnittstellentyp anstelle des Implementierungstyps zurückzugeben.
java
arraylist
collections
return-type
Tianxiang Xiong
quelle
quelle
ArrayList
ist unbekannt, und wir können diese Frage unmöglich beantworten, selbst wenn wir uns den Quellcode von ansehenCollections
.List
die Reihenfolge bei, in der die Elemente hinzugefügt werden, oder zumindest ist dies der allgemeine Vertrag, der von derList
Schnittstelle definiert wird : " Eine geordnete Sammlung (auch als Sequenz bezeichnet) ".enum
. Sie teilen einen Namen, aber sonst nichts.Antworten:
Haftungsausschluss: Ich bin kein JDK-Autor.
Ich bin damit einverstanden, dass es richtig ist, eigenen Code in Schnittstellen zu schreiben. Wenn Sie jedoch eine veränderbare Sammlung an einen Drittanbieter zurückgeben, ist es wichtig, den Drittanbieter darüber zu informieren, welche Art von Code
List
er zurückerhält.LinkedList
undArrayList
sind in Bezug auf die Leistung für verschiedene Operationen sehr unterschiedlich. Zum Beispiel kann das Entfernen des ersten Element einesArrayList
istO(n)
, aber das Entfernen des ersten Elements aLinkedList
istO(1)
.Durch die vollständige Angabe des Rückgabetyps übermitteln die JDK-Autoren in eindeutigem Code zusätzliche Informationen darüber , welche Art von Objekt sie Ihnen zurückgeben, sodass Sie Ihren Code schreiben können, um diese Methode ordnungsgemäß zu verwenden. Wenn Sie wirklich eine benötigen
LinkedList
, wissen Sie, dass Sie hier eine angeben müssen.Der Hauptgrund für das Codieren einer Schnittstelle über eine Implementierung ist, wenn Sie glauben, dass sich die Implementierung ändern wird. Die JDK-Autoren gehen wahrscheinlich davon aus, dass sie diese Methode niemals ändern werden. es wird niemals ein
LinkedList
oder ein zurückgebenCollections.UnmodifiableList
. In den meisten Fällen würden Sie jedoch wahrscheinlich immer noch Folgendes tun:quelle
toLinkedList
Methode hinzufügen , und dann wird der Namelist
für die ArrayList-Version albern aussehen.Wenn Sie zurückkehren
List
, werden Sie das Programm auf eine Schnittstelle hochstufen , was eine sehr gute Praxis ist. Dieser Ansatz hat jedoch seine Grenzen. Beispielsweise können Sie einige Methoden nicht verwenden, die fürArrayList
dieList
Schnittstelle definiert sind und in dieser nicht vorhanden sind. Weitere Informationen finden Sie in dieser Antwort .Ich zitiere das API-Design aus den Java ™ -Tutorials:
Da
ArrayList
es sich im Wesentlichen um ein Array handelt, sind sie meine erste Wahl, wenn ich ein "Sammlungsarray" benötige. Wenn ich also die Aufzählung in eine Liste konvertieren möchte, würde ich eine Array-Liste wählen.In allen anderen Fällen ist es weiterhin gültig zu schreiben:
quelle
SortedMap
fügt es einem normalen Benutzer Funktionalität hinzuMap
, daher halte ich es für sinnvoll, ihn als Rückgabetyp anzugeben. AArrayList
fügt jedoch nichts zu a hinzuList
und könnte gegen a ausgetauscht werden,LinkedList
und Sie würden nichts anderes sehen (in Bezug auf Funktionalität, nicht in Bezug auf Leistung). Trotzdem finde ich Ihre Referenz sehr interessant. +1.LinkedList
oderArrayList
. Als würde man Zeitkomplexität bekommen ..SortedMap
, sollten Sie nicht darauf zurückkommen „ weil es ist ein“. Sie sollten entscheiden, ob der Anrufer WISSEN soll, dass dies ein istSortedMap
. Und Sie sollten sich bewusst sein, dass Sie, sobald Sie a zurückgebenSortedMap
, es nie wieder auf ein gewöhnliches verallgemeinern könnenMap
- während die andere Richtung (spezifischer werden und einSortedMap
wo zurückgeben, wo ursprünglich nur einMap
zurückgegeben wurde) immer möglich ist.Funktionen, die "exklusives Eigentum" an neu erstellten veränderlichen Objekten zurückgeben, sollten häufig der spezifischste praktische Typ sein. Diejenigen, die unveränderliche Objekte zurückgeben, insbesondere wenn sie gemeinsam genutzt werden, sollten häufig weniger spezifische Typen zurückgeben.
Der Grund für die Unterscheidung ist, dass im ersteren Fall ein Objekt immer in der Lage sein wird, ein neues Objekt des angegebenen Typs zu erzeugen, und da der Empfänger das Objekt besitzt und nicht bekannt ist, welche Aktionen der Empfänger dort ausführen möchte Im Allgemeinen kann der Code, der das Objekt zurückgibt, nicht wissen, ob alternative Schnittstellenimplementierungen die Anforderungen des Empfängers erfüllen können.
Im letzteren Fall bedeutet die Tatsache, dass das Objekt unveränderlich ist, dass die Funktion möglicherweise einen alternativen Typ identifizieren kann, der alles kann, was ein komplizierterer Typ aufgrund seines genauen Inhalts tun könnte . Beispielsweise kann eine
Immutable2dMatrix
Schnittstelle von einerImmutableArrayBacked2dMatrix
Klasse und einerImmutableDiagonal2dMatrix
Klasse implementiert werden . Eine Funktion, die ein QuadratImmutable2dMatrix
zurückgeben soll, könnte entscheiden, eineImmutableDiagonalMatrix
Instanz zurückzugeben, wenn alle Elemente außerhalb der Hauptdiagonale Null sind, oderImmutableArrayBackedMatrix
wenn nicht. Der erstere Typ würde viel weniger Speicherplatz beanspruchen, aber der Empfänger sollte sich nicht um den Unterschied zwischen ihnen kümmern.Durch die Rückgabe
Immutable2dMatrix
anstelle eines konkretenImmutableArrayBackedMatrix
Codes kann der Code den Rückgabetyp basierend auf dem Inhalt des Arrays auswählen. Dies bedeutet auch, dass, wenn der Code, der das Array zurückgeben soll, eine geeignete Implementierung enthältImmutable2dMatrix
, dies einfach zurückgegeben werden kann, anstatt eine neue Instanz erstellen zu müssen . Diese beiden Faktoren können bei der Arbeit mit unveränderlichen Objekten zu großen "Gewinnen" führen.Bei der Arbeit mit veränderlichen Objekten kommt jedoch keiner der beiden Faktoren ins Spiel. Die Tatsache, dass ein veränderliches Array beim Generieren möglicherweise keine Elemente außerhalb der Hauptdiagonale enthält, bedeutet nicht, dass es niemals solche Elemente enthält. Während a
ImmutableDiagonalMatrix
effektiv ein Subtyp vonImmutable2dMatrix
aMutableDiagonalMatrix
ist , ist a folglich kein Subtyp von aMutable2dMatrix
, da das letztere Speicher außerhalb der Hauptdiagonale akzeptieren könnte, während das erstere dies nicht kann. Während unveränderliche Objekte häufig gemeinsam genutzt werden können und sollten, können veränderbare Objekte dies im Allgemeinen nicht. Eine Funktion, die nach einer neuen veränderlichen Sammlung gefragt wird, die mit bestimmten Inhalten initialisiert wurde, muss eine neue Sammlung erstellen, unabhängig davon, ob ihr Sicherungsspeicher dem angeforderten Typ entspricht oder nicht.quelle
Das Aufrufen von Methoden über eine Schnittstelle und nicht direkt über ein Objekt ist mit einem geringen Aufwand verbunden.
Dieser Overhead beträgt häufig nicht mehr als 1 oder 2 Prozessoranweisungen. Der Aufwand für den Aufruf einer Methode ist noch geringer, wenn die JIT weiß, dass die Methode endgültig ist. Dies ist für den meisten Code, den Sie und ich richtig haben, nicht messbar, aber für die Low-Level-Methoden in java.utils kann in einigen Codes verwendet werden, bei denen es sich um ein Problem handelt.
Wie bereits in anderen Antworten erwähnt, wirkt sich der konkrete Typ des zurückgegebenen Objekts (auch wenn es hinter einer Schnittstelle verborgen ist) auf die Leistung des Codes aus, der es verwendet. Diese Leistungsänderung kann sehr groß sein, so dass die aufrufende Software nicht funktioniert.
Offensichtlich haben die Autoren von
java.utils
keine Möglichkeit zu wissen, was die gesamte Software, die Collections.list () aufruft, mit dem Ergebnis macht, und keine Möglichkeit, diese Software erneut zu testen, wenn sie die Implantation von Collections.list () ändern. Daher werden sie die Implantation von Collections.list () nicht ändern, um einen anderen Listentyp zurückzugeben, selbst wenn das Typsystem dies zulässt!Wenn Sie Ihre eigene Software schreiben, haben Sie (hoffentlich) einen automatisierten Test, der Ihren gesamten Code abdeckt, und ein gutes Verständnis dafür, wie Ihre Code-Wechselbeziehungen beinhalten, wissen, wo die Leistung ein Problem darstellt. Die Möglichkeit, eine Methode zu ändern, ohne die Anrufer ändern zu müssen, ist von großem Wert, während sich das Design der Software ändert.
Daher sind die beiden Kompromisse sehr unterschiedlich.
quelle