Manejar valor nulo usando Guava MapMaker / CacheBuilder


Intento hacer una caché usando MapMaker/CacheBuilder pero no entiendo cómo manejar correctamente los valores null.

 ConcurrentMap<Key, Graph> graphs = new MapMaker()
       .concurrencyLevel(4)
       .weakKeys()
       .maximumSize(10000)
       .expireAfterWrite(10, TimeUnit.MINUTES)
       .makeComputingMap(
           new Function<Key, Graph>() {
             public Graph apply(Key key) {
               return createExpensiveGraph(key);
             }
           });

Si el método createExpensiveGraph devuelve un valor null, entonces se lanza una NullPointerException. No entiendo por qué el ComputingConcurrentHashMap arroja un NPE en lugar de devolver un valor nulo.

¿Cómo manejar esto correctamente ? Simplemente coger el NPE y devolver null en su lugar ? Me estoy perdiendo algo ?

 26
Author: skaffman, 2011-11-28

3 answers

Guava intenta obligarle a evitar usar null siempre que sea posible, porque el comportamiento inadecuado o indocumentado en presencia de null puede causar una gran cantidad de confusión y errores. Creo que definitivamente es una buena idea evitar usar nulls siempre que sea posible, y si puedes modificar tu código para que no use null, recomiendo encarecidamente ese enfoque en su lugar.

La respuesta a su pregunta depende críticamente de lo que un valor "nulo" realmente significa en su aplicación. Mas probablemente, significa que no hay "ningún valor" para esta clave, o "nada allí."En este caso, probablemente su mejor opción es utilizar Optional, envolviendo valores no nulos con Optional.de y usando Optional.absent() en lugar de null. Si debe convertir eso en un valor nulo o no nulo, puede usar Optional.orNull().

 46
Author: Louis Wasserman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-14 13:57:43

Observe cómo incluso en la declaración completa de su pregunta todavía no está claro si su intención es tener ese valor nulo almacenado en caché o no. Ya sea que CacheBuilder decidiera almacenar en caché valores nulos o no, sorprendería a muchos usuarios que esperaban lo contrario. Una vez más, null crea ambigüedades (¡eso es lo mejor!).

Así que esto es lo que haces.

  1. ¿Es posible determinar que la respuesta será "nula" sin el gasto total de createExpensiveGraph? es decir, ¿hay realmente ¿solo una simple verificación de precondición? Si es así, debe hacer eso antes de preguntar a la caché en absoluto, en cuyo momento la pregunta de si el resultado debe ser almacenado en caché o no simplemente desaparece.

  2. ¿Quieres almacenar en caché un valor nulo? A continuación, siga los consejos de Louis para utilizar Optional<T>.

  3. De lo contrario, la caché no pudo hacer su trabajo de invocar un valor correcto para la clave, y la respuesta apropiada es lanzar una excepción desde su cargador de caché (sin marcar si esto es error del programador, comprobado de otra manera). Y eso proporcionará el comportamiento que desea. Si lanzas una excepción marcada, asegúrate de usar Cache.get, no Cache.getUnchecked, en el otro lado.

 11
Author: Kevin Bourrillion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-29 16:12:23

Ver LivingWithNullHostileCollections en el wiki de Guayaba para algunas ideas sobre cómo lidiar con esto.

 9
Author: fry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-30 12:56:51