El antipatrón de inicialización de variables con dobles llaves en Java

Escrito por el .
java planeta-codigo programacion
Enlace permanente Comentarios

Java

Java es un lenguaje más verboso y con menos azúcar sintáctico (o veneno para ratas, según se mire) que otros lenguajes. Esto hace que por ejemplo para declarar e inicializar objetos tan comunes en un programa de tipo Map, List o Set que contengan un número fijo de elementos haya que escribir varias líneas de código. Estas clases de estructuras de datos del grupo de colecciones son de las más usadas en una aplicación Java. A la fecha de escribir este artículo Java no soporta literales para las colecciones que reduzca las lineas de código para inicializarlas y mejore la legibilidad del código aunque en versiones recientes si se han incorporado métodos de utilidad que cubren el requerimiento.

Con el objetivo de reducir la verbosidad quizá en algún sitio se pueda ver que usando la técnica de doble llave se puede inicializar un Map o List de una forma más reducida, tal que:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Map<String, String> map = new HashMap<>() {{
    put("hola", "hello");
    put("coche", "car");
    put("gato", "cat");
    put("casa", "house");
}};

List<String, String> list = new List<>() {{
    add("hola");
    add("coche");
    add("gato");
    add("casa");
}};
AntipatronLlaves.java

Sin embargo, no es recomendable usar este hack del lenguaje porque presenta sus inconvenientes, por ello está desaconsejado y se considera un antipatrón. En el pozo de sabiduría para el programador de StackOverflow se indican varios inconvenientes:

  • Cada bloque de inicialización con doble llave crea una clase anónima que incrementa el número de clases de la aplicación y que puede penalizar el rendimiento si se usa de forma extensiva en una aplicación.
  • Si se retorna un mapa inicializado de esta forma desde un método el mapa tendrá una referencia al objeto que lo creo, lo que evita que el objeto sea destruido por el recolector de basura hasta que no se recolecte el mapa creando una potencial fuga de memoria.

En otros hilos de StackOverflow algunas respuestas muy votadas se propone usar dobles llaves para la inicialización, pero por los puntos comentados anteriormente mejor no usarla por mucho que esté en StackOverflow y haya sido esta una respuesta muy votada.

Las alternativas en Java 8 en el caso del Map si queremos reducir la verbosidad al inicializar estos tipos de datos usados profusamente podemos usar lo siguiente, en el caso de List o Set disponemos desde hace más tiempo del método Arrays.asList:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Con Java 8
// Clase se utilidad para hacer la inicialización más simple
public class Maps {
    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
        return new AbstractMap.SimpleEntry<>(key, value);
    }

    public static <K, U> Collector<Entry<K, U>, ?, Map<K, U>> entriesToMap() {
        return Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue());
    }
}

Map<String, String> map = Stream.of(
    Maps.entry("puerta", "door"),
    Maps.entry("coche", "car"),
    Maps.entry("gato", "cat"),
    Maps.entry("casa", "house")
).collect(Maps.entriesToMap()));

List<String> list = Stream.of("puerta", "coche", "gato", "casa").collect(toList());

Set<String> set = Stream.of("puerta", "coche", "gato", "casa").collect(toSet());


MapListSetJava8.java
1
2
3
4
5
6
// Con la clase ImmutableMap de Guava
Map<String, String> map = ImmutableMap.of("puerta", "door", "choche", "car", "gato", "cat", "casa", "house");

List<String> list = Arrays.asList("puerta", "coche", "gato", "casa");

Set set = new HashSet(list);
MapListSetJava7.java

En Java 9 aunque aún no se incorporen la definición de literales al lenguaje con los métodos de utilidad of en su respectivas interfaces gracias a los defaults methods el código se simplifica en gran medida.

1
2
3
4
5
Map<String, String> map = Map.of("puerta", "door", "choche", "car", "gato", "cat", "casa", "house");

List<String> list = List.of("puerta", "coche", "gato", "casa");

Set<String> set = Set.of("puerta", "coche", "gato", "casa");
MapListSetJava9.java

También es posible lanzar excepciones checked como si fueran unchecked en Java pero al igual que en este caso no es recomendable.


Comparte el artículo: