Angenommen, ich schreibe eine Methode, die eine Map zurückgeben soll . Zum Beispiel:
public Map<String, Integer> foo() {
return new HashMap<String, Integer>();
}
Nachdem ich eine Weile darüber nachgedacht habe, habe ich entschieden, dass es keinen Grund gibt, diese Karte nach ihrer Erstellung zu ändern. Daher möchte ich eine ImmutableMap zurückgeben .
public Map<String, Integer> foo() {
return ImmutableMap.of();
}
Soll ich den Rückgabetyp als generische Map belassen oder angeben, dass ich eine ImmutableMap zurückgeben möchte?
Genau aus diesem Grund wurden Schnittstellen für erstellt. um die Implementierungsdetails auszublenden.
Auf der anderen Seite könnten andere Entwickler die Tatsache übersehen, dass dieses Objekt unveränderlich ist, wenn ich es so belasse. Somit werde ich kein Hauptziel unveränderlicher Objekte erreichen; um den Code klarer zu machen, indem die Anzahl der Objekte minimiert wird, die sich ändern können. Selbst das Schlimmste ist, dass nach einer Weile jemand versucht, dieses Objekt zu ändern. Dies führt zu einem Laufzeitfehler (der Compiler warnt nicht davor).
return ImmutableMap.of(somethingElse)
. Stattdessen würden Sie dasImmutableMap.of(somethingElse)
als Feld speichern und nur dieses Feld zurückgeben. All dies beeinflusst das Design hier.Antworten:
Wenn Sie eine öffentlich zugängliche API schreiben und diese Unveränderlichkeit ein wichtiger Aspekt Ihres Entwurfs ist, würde ich dies definitiv explizit machen, indem der Name der Methode eindeutig angibt, dass die zurückgegebene Karte unveränderlich ist, oder indem der konkrete Typ von zurückgegeben wird die Karte. Es reicht meiner Meinung nach nicht aus, es im Javadoc zu erwähnen.
Da Sie anscheinend die Guava-Implementierung verwenden, habe ich mir das Dokument angesehen und es ist eine abstrakte Klasse, sodass Sie ein wenig Flexibilität hinsichtlich des tatsächlichen, konkreten Typs erhalten.
Wenn Sie ein internes Tool / eine interne Bibliothek schreiben, ist es viel akzeptabler, nur eine Ebene zurückzugeben
Map
. Die Leute kennen die Interna des Codes, den sie aufrufen, oder haben zumindest einfachen Zugriff darauf.Mein Fazit wäre, dass explizit gut ist, überlasse die Dinge nicht dem Zufall.
quelle
Map
Schnittstellentyp oder denImmutableMap
Implementierungstyp zurückgeben soll.ImmutableMap
das Guava-Team,ImmutableMap
eine Schnittstelle mit semantischen Garantien in Betracht zu ziehen und diesen Typ direkt zurückzugeben.Sie sollten
ImmutableMap
als Rückgabetyp haben.Map
enthält Verfahren , die durch die Umsetzung des nicht unterstützt werdenImmutableMap
( zum Beispielput
) , und sind gekennzeichnet@deprecated
inImmutableMap
.Die Verwendung veralteter Methoden führt zu einer Compiler-Warnung und die meisten IDEs warnen, wenn Benutzer versuchen, die veralteten Methoden zu verwenden.
Diese erweiterte Warnung ist Laufzeitausnahmen vorzuziehen, da dies Ihr erster Hinweis darauf ist, dass etwas nicht stimmt.
quelle
Collections.immutableMap
dann?List
gibt es keine Garantie,List::add
die gültig ist. Versuchen Sie esArrays.asList("a", "b").add("c");
. Es gibt keinen Hinweis darauf, dass es zur Laufzeit fehlschlägt, dies ist jedoch der Fall. Zumindest gibt dir Guave einen Kopf hoch.Das sollten Sie in den Javadocs erwähnen. Entwickler lesen sie, wissen Sie.
Kein Entwickler veröffentlicht seinen Code ungetestet. Und wenn er es testet, wird eine Ausnahme ausgelöst, bei der er nicht nur den Grund, sondern auch die Datei und Zeile sieht, in der er versucht hat, auf eine unveränderliche Karte zu schreiben.
Beachten Sie jedoch, dass nur das
Map
selbst unveränderlich ist, nicht die darin enthaltenen Objekte.quelle
Das stimmt, aber andere Entwickler sollten ihren Code testen und sicherstellen, dass er abgedeckt ist.
Trotzdem haben Sie 2 weitere Möglichkeiten, um dies zu lösen:
Verwenden Sie Javadoc
Wählen Sie einen beschreibenden Methodennamen
Für einen konkreten Anwendungsfall können Sie die Methoden sogar besser benennen. Z.B
Was kannst du sonst noch tun?!
Sie können a zurückgeben
Kopieren
Dieser Ansatz sollte verwendet werden, wenn Sie erwarten, dass viele Clients die Karte ändern und solange die Karte nur wenige Einträge enthält.
CopyOnWriteMap
Copy-on-Write-Sammlungen werden normalerweise verwendet, wenn Sie sich damit befassen müssen
Parallelität . Das Konzept hilft Ihnen aber auch in Ihrer Situation, da eine CopyOnWriteMap eine Kopie der internen Datenstruktur bei einer mutativen Operation erstellt (z. B. Hinzufügen, Entfernen).
In diesem Fall benötigen Sie einen Thin Wrapper um Ihre Map, der alle Methodenaufrufe mit Ausnahme der Mutationsoperationen an die zugrunde liegende Map delegiert. Wenn eine Mutationsoperation aufgerufen wird, wird eine Kopie der zugrunde liegenden Karte erstellt, und alle weiteren Aufrufe werden an diese Kopie delegiert.
Dieser Ansatz sollte verwendet werden, wenn Sie erwarten, dass einige Clients die Karte ändern.
Leider hat Java keine solche
CopyOnWriteMap
. Möglicherweise finden Sie jedoch einen Dritten oder implementieren ihn selbst.Zuletzt sollten Sie bedenken, dass die Elemente in Ihrer Karte möglicherweise noch veränderbar sind.
quelle
Geben Sie auf jeden Fall eine ImmutableMap zurück. Die Begründung lautet:
quelle
Das hängt von der Klasse selbst ab. Guavas
ImmutableMap
soll kein unveränderlicher Blick in eine veränderliche Klasse sein. Wenn Ihre Klasse unveränderlich ist und eine Struktur hat, die im Grunde genommen eine istImmutableMap
, geben Sie den Rückgabetyp einImmutableMap
. Wenn Ihre Klasse jedoch veränderlich ist, tun Sie dies nicht. Wenn Sie dies haben:Guave kopiert die Karte jedes Mal. Das ist langsam. Aber wenn
internalMap
es schon ein warImmutableMap
, dann ist es völlig in Ordnung.Wenn Sie Ihre Klasse nicht auf die Rückkehr beschränken
ImmutableMap
, können Sie stattdessenCollections.unmodifiableMap
wie folgt zurückkehren:Beachten Sie, dass dies unveränderlich ist Ansicht in der Karte ist. Wenn sich dies
internalMap
ändert, wird auch eine zwischengespeicherte Kopie von geändertCollections.unmodifiableMap(internalMap)
. Ich bevorzuge es jedoch immer noch für Getter.quelle
unmodifiableMap
nur vom Inhaber der Referenz nicht geändert werden kann - es handelt sich um eine Ansicht, und der Inhaber der Hintergrundkarte kann die Hintergrundkarte ändern, und dann ändert sich die Ansicht. Das ist also eine wichtige Überlegung.Collections.unmodifiableMap
. Diese Methode gibt eine Ansicht auf die ursprüngliche Karte zurück, anstatt ein separates neues Kartenobjekt zu erstellen. Jeder andere Code, der auf die ursprüngliche Karte verweist, kann sie ändern, und dann lernt der Inhaber der angeblich nicht veränderbaren Karte auf die harte Tour, dass die Karte tatsächlich unter ihnen geändert werden kann. Ich bin selbst von dieser Tatsache gebissen worden. Ich habe gelernt, entweder eine Kopie der Karte zurückzugeben oder Guava zu verwendenImmutableMap
.Dies beantwortet nicht die genaue Frage, aber es lohnt sich dennoch zu überlegen, ob eine Karte überhaupt zurückgegeben werden sollte. Wenn die Karte unveränderlich ist, basiert die primäre Methode, die bereitgestellt werden soll, auf get (key):
Dies macht die API viel enger. Wenn tatsächlich eine Karte erforderlich ist, kann dies dem Client der API überlassen werden, indem ein Strom von Einträgen bereitgestellt wird:
Anschließend kann der Client nach Bedarf eine eigene unveränderliche oder veränderbare Karte erstellen oder die Einträge zu einer eigenen Karte hinzufügen. Wenn der Client wissen muss, ob der Wert vorhanden ist, kann stattdessen optional zurückgegeben werden:
quelle
Unveränderliche Karte ist eine Art Karte. Das Verlassen des Rückgabetyps der Karte ist also in Ordnung.
Um sicherzustellen, dass die Benutzer das zurückgegebene Objekt nicht ändern, kann die Dokumentation der Methode die Merkmale des zurückgegebenen Objekts beschreiben.
quelle
Dies ist wohl Ansichtssache, aber die bessere Idee ist hier, eine Schnittstelle für die Kartenklasse zu verwenden. Diese Schnittstelle muss nicht explizit angeben, dass sie unveränderlich ist, aber die Nachricht ist dieselbe, wenn Sie keine der Setter-Methoden der übergeordneten Klasse in der Schnittstelle verfügbar machen.
Schauen Sie sich den folgenden Artikel an:
Andy Gibson
quelle
Map
Schnittstelle nicht erweitern (oder implementieren) , besteht darin, dass Sie sie nicht an eine API-Funktion übergeben können, die eine erwartet,Map
ohne sie zu übertragen. Dies schließt alle Arten von Kopierkonstruktoren anderer Arten von Karten ein. Wenn die Veränderlichkeit nur durch die Benutzeroberfläche "verborgen" wird, können Ihre Benutzer dies außerdem jederzeit umgehen, indem Sie Ihren Code auf diese Weise umwandeln und brechen.