java - icon - ¿Cómo deberíamos administrar la secuencia jdk8 para valores nulos?
redimensionar icon java (5)
Hola compañeros desarrolladores de Java,
Sé que el tema puede estar un poco in advance
ya que el JDK8 aún no se ha lanzado (y no por ahora, de todos modos ...) pero estaba leyendo algunos artículos sobre las expresiones Lambda y particularmente la parte relacionada con la nueva API de recopilación conocida como Stream.
Este es el ejemplo dado en el artículo de Java Magazine (es un algoritmo de población de nutria ...):
Set<Otter> otters = getOtters();
System.out.println(otters.stream()
.filter(o -> !o.isWild())
.map(o -> o.getKeeper())
.filter(k -> k.isFemale())
.into(new ArrayList<>())
.size());
Mi pregunta es ¿qué ocurre si en el medio de la iteración interna del conjunto, uno de la nutria es nulo?
Esperaría que se lanzara una NullPointerException, pero quizás todavía estoy atrapado en el paradigma de desarrollo anterior (no funcional). ¿Alguien me puede aclarar cómo se debe manejar esto?
Si esto realmente arroja una NullPointerException, creo que la característica es bastante peligrosa y deberá usarse solo de la siguiente manera:
- Desarrollador para asegurarse de que no hay ningún valor nulo (tal vez usando un .filter previo (o -> o! = Null))
- Desarrollador para garantizar que la aplicación nunca genera nutria nula o un objeto NullOtter especial para tratar.
¿Cuál es la mejor opción o cualquier otra opción?
¡Gracias!
Un ejemplo de cómo evitar el nulo, por ejemplo, usar filtro antes de agruparBy
Filtra las instancias nulas antes de agruparBy.
Aquí hay un ejemploMyObjectlist.stream()
.filter(p -> p.getSomeInstance() != null)
.collect(Collectors.groupingBy(MyObject::getSomeInstance));
Si solo desea filtrar valores nulos de una secuencia, simplemente puede usar una referencia de método a java.util.Objects.nonNull (Object) . De su documentación:
Este método existe para ser usado como un predicado ,
filter(Objects::nonNull)
Por ejemplo:
List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null);
list.stream()
.filter( Objects::nonNull ) // <-- Filter out null values
.forEach( System.out::println );
Esto se imprimirá:
Foo
Bar
El pensamiento actual parece ser "tolerar" nulos, es decir, permitirlos en general, aunque algunas operaciones son menos tolerantes y pueden terminar tirando NPE. Vea la discusión de los nulos en la lista de correo del grupo de expertos de Lambda Libraries, específicamente este mensaje . El consenso alrededor de la opción n. ° 3 surgió posteriormente (con una notable objeción de Doug Lea). Entonces, sí, la preocupación del OP por las tuberías que explotan con NPE es válida.
No es por nada que Tony Hoare se refirió a los nulos como el "error de los mil millones de dólares". Tratar con nulos es un verdadero dolor. Incluso con colecciones clásicas (sin considerar lambdas o streams) los nulos son problemáticos. Como se menciona en un comentario, algunas colecciones permiten nulos y otras no. Con colecciones que permiten nulos, esto introduce ambigüedades en la API. Por ejemplo, con Map.get () , un retorno nulo indica que la clave está presente y su valor es nulo, o que la clave está ausente. Uno tiene que hacer un trabajo extra para eliminar la ambigüedad de estos casos.
El uso habitual para null es denotar la ausencia de un valor. El enfoque para tratar con esto propuesto para Java SE 8 es introducir un nuevo tipo java.util.Optional
, que encapsula la presencia / ausencia de un valor, junto con los comportamientos de suministro de un valor predeterminado, o lanzar una excepción, o llamar a un función, etc. si el valor está ausente. Optional
embargo, Optional
solo se usan las nuevas API, todo lo demás en el sistema aún tiene que soportar la posibilidad de nulos.
Mi consejo es evitar referencias nulas reales en la mayor medida posible. Es difícil ver el ejemplo dado de cómo podría haber una Nutria "nula". Pero si uno fuera necesario, las sugerencias de OP de filtrar valores nulos o mapearlos a un objeto centinela (el Patrón de Objeto Nulo ) son buenos enfoques.
La respuesta de Stuart proporciona una gran explicación, pero me gustaría brindar otro ejemplo.
Me encontré con este problema cuando intentaba realizar una reduce
en un Stream que contenía valores nulos (en realidad era LongStream.average()
, que es un tipo de reducción). Como average () devuelve OptionalDouble
, asumí que Stream podía contener nulos pero en su lugar se lanzó una NullPointerException. Esto se debe a la explicación de Stuart de null v. Empty.
Entonces, como OP sugiere, agregué un filtro como ese:
list.stream()
.filter(o -> o != null)
.reduce(..);
O como los tangentes se señalan a continuación, utilice el predicado proporcionado por la API de Java:
list.stream()
.filter(Objects::nonNull)
.reduce(..);
De la discusión de la lista de correos Stuart enlazó: Brian Goetz sobre nulos en Streams
Aunque las respuestas son 100% correctas, una pequeña sugerencia para manejar el caso nulo de la lista con Opcional :
List<String> listOfStuffFiltered = Optional.ofNullable(listOfStuff)
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
La parte Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList)
le permitirá manejar bien el caso cuando listOfStuff
es nulo y devolver una lista vacía en lugar de fallar con NullPointerException.