Digamos que estoy escribiendo un método que debería devolver un mapa . Por ejemplo:
public Map<String, Integer> foo() {
return new HashMap<String, Integer>();
}
Después de pensarlo un rato, he decidido que no hay razón para modificar este mapa una vez creado. Por lo tanto, me gustaría devolver un ImmutableMap .
public Map<String, Integer> foo() {
return ImmutableMap.of();
}
¿Debo dejar el tipo de devolución como un mapa genérico o debo especificar que estoy devolviendo un mapa inmutable?
Por un lado, esta es exactamente la razón por la que se crearon las interfaces; para ocultar los detalles de implementación.
Por otro lado, si lo dejo así, otros desarrolladores podrían perder el hecho de que este objeto es inmutable. Por lo tanto, no lograré un objetivo importante de objetos inmutables; para hacer el código más claro minimizando el número de objetos que pueden cambiar. Peor aún, después de un tiempo, alguien podría intentar cambiar este objeto, y esto resultará en un error de tiempo de ejecución (el compilador no lo advertirá).
return ImmutableMap.of(somethingElse)
. En su lugar, almacenaría elImmutableMap.of(somethingElse)
como un campo y devolvería solo este campo. Todo esto influye en el diseño aquí.Respuestas:
Si está escribiendo una API de cara al público y esa inmutabilidad es un aspecto importante de su diseño, definitivamente lo haría explícito si el nombre del método denota claramente que el mapa devuelto será inmutable o devolviendo el tipo concreto de el mapa. En mi opinión, mencionarlo en el javadoc no es suficiente.
Dado que aparentemente está utilizando la implementación de Guava, miré el documento y es una clase abstracta, por lo que le brinda un poco de flexibilidad en el tipo concreto real.
Si está escribiendo una herramienta / biblioteca interna, es mucho más aceptable devolver un archivo
Map
. Las personas conocerán los aspectos internos del código al que están llamando o, al menos, tendrán fácil acceso a él.Mi conclusión sería que lo explícito es bueno, no dejes las cosas al azar.
fuente
Map
tipo de interfaz o elImmutableMap
tipo de implementación.ImmutableMap
, el consejo del equipo de Guava es considerarImmutableMap
una interfaz con garantías semánticas, y que debe devolver ese tipo directamente.Debería tener
ImmutableMap
como tipo de devolución.Map
contiene métodos que no son compatibles con la implementación deImmutableMap
(egput
) y están marcados@deprecated
enImmutableMap
.El uso de métodos obsoletos dará como resultado una advertencia del compilador y la mayoría de los IDE advertirán cuando las personas intenten utilizar los métodos obsoletos.
Esta advertencia avanzada es preferible a tener excepciones en tiempo de ejecución como primer indicio de que algo anda mal.
fuente
Collections.immutableMap
entonces?List
, no hay garantía de queList::add
sea válida. PruebaArrays.asList("a", "b").add("c");
. No hay ninguna indicación de que fallará en tiempo de ejecución, pero lo hace. Al menos Guava te avisa.Deberías mencionar eso en los javadocs. Los desarrolladores los leen, ya sabes.
Ningún desarrollador publica su código sin probarlo. Y cuando lo prueba, recibe una excepción en la que no solo ve el motivo, sino también el archivo y la línea donde intentó escribir en un mapa inmutable.
Sin embargo, tenga en cuenta que solo el
Map
mismo será inmutable, no los objetos que contiene.fuente
Eso es cierto, pero otros desarrolladores deberían probar su código y asegurarse de que esté cubierto.
Sin embargo, tienes 2 opciones más para solucionar esto:
Utilice Javadoc
Elija un nombre de método descriptivo
Para un caso de uso concreto, incluso puede nombrar mejor los métodos. P.ej
¡¿Qué más puedes hacer?!
Puede devolver un
Copiar
Este enfoque debe usarse si espera que muchos clientes modifiquen el mapa y siempre que el mapa solo contenga algunas entradas.
CopyOnWriteMap
Las colecciones de copia en escritura se utilizan generalmente cuando tienes que lidiar con
concurrencia. Pero el concepto también le ayudará en su situación, ya que CopyOnWriteMap crea una copia de la estructura de datos interna en una operación mutativa (por ejemplo, agregar, eliminar).
En este caso, necesita un envoltorio delgado alrededor de su mapa que delegue todas las invocaciones de métodos al mapa subyacente, excepto las operaciones mutativas. Si se invoca una operación mutativa, se crea una copia del mapa subyacente y todas las invocaciones posteriores se delegarán en esta copia.
Este enfoque debe utilizarse si espera que algunos clientes modifiquen el mapa.
Lamentablemente, Java no tiene tal
CopyOnWriteMap
. Pero puede encontrar un tercero o implementarlo usted mismo.Por último, debe tener en cuenta que los elementos de su mapa aún pueden ser mutables.
fuente
Definitivamente devuelve un ImmutableMap, la justificación es:
fuente
Depende de la clase en sí. Guava's
ImmutableMap
no pretende ser una vista inmutable en una clase mutable. Si su clase es inmutable y tiene una estructura que es básicamente anImmutableMap
, entonces haga el tipo de retornoImmutableMap
. Sin embargo, si tu clase es mutable, no lo hagas. Si tienes esto:Guava copiará el mapa cada vez. Eso es lento. Pero si
internalMap
ya era unImmutableMap
, entonces está totalmente bien.Si no restringe su clase para que regrese
ImmutableMap
, entonces podría regresarCollections.unmodifiableMap
así:Tenga en cuenta que esta es una vista inmutable del mapa. Si
internalMap
cambia, también lo hará una copia en caché deCollections.unmodifiableMap(internalMap)
. Sin embargo, todavía lo prefiero para los getters.fuente
unmodifiableMap
titular de la referencia solo no puede modificarlo: es una vista y el titular del mapa de respaldo puede modificar el mapa de respaldo y luego la vista cambia. Entonces esa es una consideración importante.Collections.unmodifiableMap
. Ese método devuelve una vista del mapa original en lugar de crear un nuevo objeto de mapa distinto e independiente. Entonces, cualquier otro código que haga referencia al mapa original puede modificarlo, y luego el poseedor del mapa supuestamente no modificable aprende por las malas que el mapa puede modificarse desde debajo de ellos. A mí me ha mordido ese hecho. Aprendí a devolver una copia del mapa o usar GuavaImmutableMap
.Esto no responde a la pregunta exacta, pero vale la pena considerar si se debe devolver un mapa. Si el mapa es inmutable, entonces el método principal que se proporcionará se basa en get (clave):
Esto hace que la API sea mucho más estricta. Si realmente se requiere un mapa, esto podría dejarse en manos del cliente de la API proporcionando un flujo de entradas:
Luego, el cliente puede hacer su propio mapa inmutable o mutable según lo necesite, o agregar las entradas a su propio mapa. Si el cliente necesita saber si el valor existe, se puede devolver opcional en su lugar:
fuente
Immutable Map es un tipo de mapa. Entonces, dejar el tipo de retorno de Map está bien.
Para asegurarse de que los usuarios no modifiquen el objeto devuelto, la documentación del método puede describir las características del objeto devuelto.
fuente
Podría decirse que esto es una cuestión de opinión, pero la mejor idea aquí es usar una interfaz para la clase de mapa. Esta interfaz no necesita decir explícitamente que es inmutable, pero el mensaje será el mismo si no expone ninguno de los métodos de establecimiento de la clase principal en la interfaz.
Eche un vistazo al siguiente artículo:
Andy Gibson
fuente
Map
interfaz es que no puedes pasarlo a ninguna función de API esperando unMap
sin emitirlo. Esto incluye todo tipo de constructores de copias de otros tipos de mapas. Además de eso, si la mutabilidad sólo está "oculta" por la interfaz, sus usuarios siempre pueden solucionar esto enviando y rompiendo su código de esa manera.