Disons que j'écris une méthode qui devrait renvoyer une Map . Par exemple:
public Map<String, Integer> foo() {
return new HashMap<String, Integer>();
}
Après y avoir réfléchi pendant un moment, j'ai décidé qu'il n'y avait aucune raison de modifier cette carte une fois qu'elle est créée. Ainsi, je voudrais retourner un ImmutableMap .
public Map<String, Integer> foo() {
return ImmutableMap.of();
}
Dois-je laisser le type de retour comme une carte générique, ou dois-je spécifier que je retourne une ImmutableMap?
D'un côté, c'est exactement pourquoi les interfaces ont été créées pour; pour masquer les détails d'implémentation.
D'un autre côté, si je laisse ça comme ça, d'autres développeurs pourraient manquer le fait que cet objet est immuable. Ainsi, je n'atteindrai pas un objectif majeur des objets immuables; pour rendre le code plus clair en minimisant le nombre d'objets qui peuvent changer. Pire encore, après un certain temps, quelqu'un pourrait essayer de changer cet objet, ce qui entraînera une erreur d'exécution (le compilateur ne l'avertira pas).
return ImmutableMap.of(somethingElse)
. Au lieu de cela, vous stockerez leImmutableMap.of(somethingElse)
sous forme de champ et ne renverriez que ce champ. Tout cela influence le design ici.Réponses:
Si vous écrivez une API publique et que l'immuabilité est un aspect important de votre conception, je le rendrais certainement explicite soit en faisant en sorte que le nom de la méthode indique clairement que la carte retournée sera immuable ou en renvoyant le type concret de la carte. Le mentionner dans le javadoc ne suffit pas à mon avis.
Puisque vous utilisez apparemment l'implémentation de Guava, j'ai regardé le document et c'est une classe abstraite donc cela vous donne un peu de flexibilité sur le type concret réel.
Si vous écrivez un outil / une bibliothèque interne, il devient beaucoup plus acceptable de simplement renvoyer un simple
Map
. Les gens connaîtront les composants internes du code qu'ils appellent ou du moins y auront facilement accès.Ma conclusion serait qu'explicite est une bonne chose, ne laissez pas les choses au hasard.
la source
Map
type d'interface ou leImmutableMap
type d'implémentation.ImmutableMap
, le conseil de l'équipe Guava est d'envisagerImmutableMap
une interface avec des garanties sémantiques, et que vous devriez retourner ce type directement.Vous devriez avoir
ImmutableMap
comme type de retour.Map
contient des méthodes qui ne sont pas prises en charge par l'implémentation deImmutableMap
(par exempleput
) et qui sont marquées@deprecated
dansImmutableMap
.L'utilisation de méthodes obsolètes entraînera un avertissement du compilateur et la plupart des IDE avertiront lorsque des personnes tenteront d'utiliser les méthodes obsolètes.
Cet avertissement avancé est préférable aux exceptions d'exécution comme premier indice que quelque chose ne va pas.
la source
Collections.immutableMap
alors?List
, il n'y a aucune garantieList::add
valable. EssayezArrays.asList("a", "b").add("c");
. Il n'y a aucune indication qu'il échouera à l'exécution, mais c'est le cas. Au moins, Guava vous avertit.Vous devriez le mentionner dans les javadocs. Les développeurs les lisent, vous savez.
Aucun développeur ne publie son code non testé. Et quand il le teste, il obtient une exception lancée où il voit non seulement la raison, mais aussi le fichier et la ligne où il a essayé d'écrire sur une carte immuable.
Notez cependant que seul le
Map
lui - même sera immuable, pas les objets qu'il contient.la source
C'est vrai, mais les autres développeurs devraient tester leur code et s'assurer qu'il est couvert.
Néanmoins, vous avez 2 autres options pour résoudre ce problème:
Utiliser Javadoc
Choisissez un nom de méthode descriptif
Pour un cas d'utilisation concret, vous pouvez même mieux nommer les méthodes. Par exemple
Que pouvez vous faire d'autre?!
Vous pouvez retourner un
copie
Cette approche doit être utilisée si vous prévoyez que de nombreux clients modifieront la carte et tant que la carte ne contient que quelques entrées.
CopyOnWriteMap
Les collections copy on write sont généralement utilisées lorsque vous devez gérer la
concurrence. Mais le concept vous aidera également dans votre situation, car un CopyOnWriteMap crée une copie de la structure de données interne sur une opération mutative (par exemple, ajouter, supprimer).
Dans ce cas, vous avez besoin d'un wrapper fin autour de votre carte qui délègue tous les appels de méthode à la carte sous-jacente, à l'exception des opérations mutatives. Si une opération mutative est appelée, elle crée une copie de la carte sous-jacente et toutes les autres invocations seront déléguées à cette copie.
Cette approche doit être utilisée si vous prévoyez que certains clients modifieront la carte.
Malheureusement, java n'a pas un tel fichier
CopyOnWriteMap
. Mais vous pouvez trouver un tiers ou le mettre en œuvre vous-même.Enfin, vous devez garder à l'esprit que les éléments de votre carte peuvent encore être modifiables.
la source
Renvoyez définitivement un ImmutableMap, la justification étant:
la source
Cela dépend de la classe elle-même. Guava
ImmutableMap
n'est pas destiné à être une vue immuable dans une classe mutable. Si votre classe est immuable et a une structure qui est essentiellement anImmutableMap
, alors créez le type de retourImmutableMap
. Cependant, si votre classe est modifiable, ne le faites pas. Si vous avez ceci:Guava copiera la carte à chaque fois. C'est lent. Mais si
internalMap
c'était déjà unImmutableMap
, alors tout va bien.Si vous ne limitez pas votre classe au retour
ImmutableMap
, vous pouvez à la place revenirCollections.unmodifiableMap
comme suit:Notez qu'il s'agit d'une vue immuable de la carte. En cas de
internalMap
changement, une copie en cache deCollections.unmodifiableMap(internalMap)
. Cependant, je le préfère toujours pour les getters.la source
unmodifiableMap
n'est modifiable que par le détenteur de la référence - c'est une vue et le détenteur de la backing map peut modifier la backing map puis la vue change. C'est donc une considération importante.Collections.unmodifiableMap
. Cette méthode renvoie une vue sur la carte d'origine plutôt que de créer un nouvel objet de carte distinct et distinct. Ainsi, tout autre code référençant la carte originale peut la modifier, et alors le détenteur de la carte supposée non modifiable apprend à la dure que la carte peut en effet être modifiée à partir d'eux. J'ai moi-même été mordu par ce fait. J'ai appris à renvoyer une copie de la carte ou à utiliser GuavaImmutableMap
.Cela ne répond pas à la question exacte, mais cela vaut toujours la peine de se demander si une carte doit être retournée. Si la carte est immuable, la méthode principale à fournir est basée sur get (key):
Cela rend l'API beaucoup plus stricte. Si une carte est réellement requise, cela pourrait être laissé au client de l'API en fournissant un flux d'entrées:
Ensuite, le client peut créer sa propre carte immuable ou mutable selon ses besoins, ou ajouter les entrées à sa propre carte. Si le client a besoin de savoir si la valeur existe, facultatif peut être renvoyé à la place:
la source
La carte immuable est un type de carte. Donc, laisser le type de retour de Map est correct.
Pour s'assurer que les utilisateurs ne modifient pas l'objet retourné, la documentation de la méthode peut décrire les caractéristiques de l'objet retourné.
la source
C'est sans doute une question d'opinion, mais la meilleure idée ici est d'utiliser une interface pour la classe map. Cette interface n'a pas besoin de dire explicitement qu'elle est immuable, mais le message sera le même si vous n'exposez aucune des méthodes setter de la classe parent dans l'interface.
Jetez un œil à l'article suivant:
Andy Gibson
la source
Map
interface est que vous ne pouvez pas le transmettre à une fonction API qui attend unMap
sans le lancer. Cela inclut toutes sortes de constructeurs de copie d'autres types de cartes. En plus de cela, si la mutabilité est uniquement «masquée» par l'interface, vos utilisateurs peuvent toujours contourner ce problème en castant et en cassant votre code de cette façon.