¿Por qué esto arroja un java.lang.NullPointerException?
List<String> strings = new ArrayList<>();
strings.add(null);
strings.add("test");
String firstString = strings.stream()
.findFirst() // Exception thrown here
.orElse("StringWhenListIsEmpty");
//.orElse(null); // Changing the `orElse()` to avoid ambiguity
El primer artículo stringses null, que es un valor perfectamente aceptable. Además, findFirst()devuelve un Optional , que tiene aún más sentido para findFirst()poder manejar nulls.
EDITAR: actualizado orElse()para que sea menos ambiguo.
java
java-8
java-stream
optional
interminable
fuente
fuente

Stringaquí, ¿qué pasa si es una lista que representa una columna en la base de datos? El valor de la primera fila de esa columna puede sernull.nulles un valor perfectamente aceptable en Java, en términos generales. En particular, es un elemento válido para unArrayList<String>. Sin embargo, como cualquier otro valor, existen limitaciones sobre lo que se puede hacer con él. "No lo use nuncanull" no es un consejo útil, ya que no puede evitarlo.findFirst(), no hay nada más que quieras hacer.Respuestas:
La razón de esto es el uso de
Optional<T>en la devolución. Opcional no se permite contenernull. Esencialmente, no ofrece ninguna forma de distinguir las situaciones "no está ahí" y "está ahí, pero está configurado paranull".Es por eso que la documentación prohíbe explícitamente la situación cuando
nullse selecciona enfindFirst():fuente
Optional. De todos modos, creo que estoy al borde de despotricar: si el idioma no lo admite, no lo admite.booleanpara diferenciar estas dos situaciones tendría mucho sentido. Para mí, parece que el uso deOptional<T>aquí fue una elección cuestionable.Iterabletipo, verificahasNext()y devuelve el valor apropiado.findFirstdevuelve un valor opcional vacío en el caso de POComo ya se comentó , los diseñadores de API no asumen que el desarrollador quiera tratar los
nullvalores y los valores ausentes de la misma manera.Si aún desea hacer eso, puede hacerlo explícitamente aplicando la secuencia
a la corriente. El resultado será un opcional vacío en ambos casos, si no hay primer elemento o si el primer elemento lo es
null. Entonces, en su caso, puede usarString firstString = strings.stream() .map(Optional::ofNullable).findFirst().flatMap(Function.identity()) .orElse(null);para obtener un
nullvalor si el primer elemento está ausente onull.Si desea distinguir entre estos casos, simplemente puede omitir el
flatMappaso:Optional<String> firstString = strings.stream() .map(Optional::ofNullable).findFirst().orElse(null); System.out.println(firstString==null? "no such element": firstString.orElse("first element is null"));Esto no es muy diferente a su pregunta actualizada. Solo tienes que reemplazar
"no such element"con"StringWhenListIsEmpty"y"first element is null"connull. Pero si no te gustan los condicionales, puedes lograrlo también como:String firstString = strings.stream().skip(0) .map(Optional::ofNullable).findFirst() .orElseGet(()->Optional.of("StringWhenListIsEmpty")) .orElse(null);Ahora,
firstStringseránullsi un elemento existe pero existenully será"StringWhenListIsEmpty"cuando no exista ningún elemento.fuente
nullpara 1) el primer elemento esnullo 2) no existen elementos dentro de la lista. He actualizado la pregunta para eliminar la ambigüedad.Optionalse puede asignar unnull. Dado queOptionalse supone que es un "tipo de valor", nunca debe ser nulo. Y un Opcional nunca debe ser comparado por==. El código puede fallar en Java 10 :) o siempre que se introduzca un tipo de valor en Java.nully aunque las instancias nunca deben ser comparadas==, la referencia puede ser probada para sunulluso,==ya que esa es la única forma de probarlanull. No veo cómonulldebería funcionar una transición de este tipo a "nunca " para el código existente, ya que incluso el valor predeterminado para todas las variables de instancia y elementos de matriz esnull. El fragmento seguramente no es el mejor código, pero tampoco la tarea de tratarnulls como valores presentes.null. Sin embargo, dado que un cambio de lenguaje tan hipotético haría que el compilador emitiera un error aquí (no romper el código silenciosamente), puedo vivir con el hecho de que posiblemente tendría que ser adaptado para Java 10. Supongo que laStreamAPI también se ven bastante diferentes…Puede utilizar
java.util.Objects.nonNullpara filtrar la lista antes de buscaralgo como
fuente
firstStringserlonullsi el primer elementostringsesnull.Optional.ofque no es seguro nulo. Usted podríamapaOptional.ofNullabley luego usarfindFirst, pero el resultado final será con un opcional de opcionalLa información siguiente sustituye código
findFirst()conlimit(1)y reemplazaorElse()conreduce():String firstString = strings. stream(). limit(1). reduce("StringWhenListIsEmpty", (first, second) -> second);limit()permite que solo 1 elemento alcancereduce. ElBinaryOperatorpasado areducedevuelve ese elemento 1 o"StringWhenListIsEmpty"si ningún elemento alcanza elreduce.La belleza de esta solución es que
Optionalno está asignada y laBinaryOperatorlambda no va a asignar nada.fuente
Se supone que opcional es un tipo de "valor". (lea la letra pequeña en javadoc :) JVM podría incluso reemplazar todo
Optional<Foo>con soloFoo, eliminando todos los costos de empaquetado y desembalaje. UnnullFoo significa un vacíoOptional<Foo>.Es un diseño posible permitir Opcional con valor nulo, sin agregar una bandera booleana; solo agregue un objeto centinela. (incluso podría usarse
thiscomo centinela; ver la causa Throwable.)La decisión de que Opcional no puede envolver nulos no se basa en el costo del tiempo de ejecución. Este fue un tema muy controvertido y necesita buscar en las listas de correo. La decisión no convence a todo el mundo.
En cualquier caso, dado que Optional no puede envolver un valor nulo, nos empuja a una esquina en casos como
findFirst. Deben haber razonado que los valores nulos son muy raros (incluso se consideró que Stream debería prohibir los valores nulos), por lo tanto, es más conveniente lanzar una excepción en valores nulos en lugar de en streams vacíos.Una solución es boxear
null, por ejemploclass Box<T> static Box<T> of(T value){ .. } Optional<Box<String>> first = stream.map(Box::of).findFirst();(Dicen que la solución a todos los problemas de programación orientada a objetos es introducir otro tipo :)
fuente
Boxtipo. ElOptionaltipo en sí puede servir para este propósito. Vea mi respuesta como ejemplo.nulles un valor válido como cualquier otro, sin tratamiento especial para él. (hasta algún tiempo después :)