¿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 strings
es null
, que es un valor perfectamente aceptable. Además, findFirst()
devuelve un Optional , que tiene aún más sentido para findFirst()
poder manejar null
s.
EDITAR: actualizado orElse()
para que sea menos ambiguo.
java
java-8
java-stream
optional
interminable
fuente
fuente
String
aquí, ¿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
.null
es 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
null
se selecciona enfindFirst()
:fuente
Optional
. De todos modos, creo que estoy al borde de despotricar: si el idioma no lo admite, no lo admite.boolean
para diferenciar estas dos situaciones tendría mucho sentido. Para mí, parece que el uso deOptional<T>
aquí fue una elección cuestionable.Iterable
tipo, verificahasNext()
y devuelve el valor apropiado.findFirst
devuelve 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
null
valores 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
null
valor si el primer elemento está ausente onull
.Si desea distinguir entre estos casos, simplemente puede omitir el
flatMap
paso: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,
firstString
seránull
si un elemento existe pero existenull
y será"StringWhenListIsEmpty"
cuando no exista ningún elemento.fuente
null
para 1) el primer elemento esnull
o 2) no existen elementos dentro de la lista. He actualizado la pregunta para eliminar la ambigüedad.Optional
se puede asignar unnull
. Dado queOptional
se 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.null
y aunque las instancias nunca deben ser comparadas==
, la referencia puede ser probada para sunull
uso,==
ya que esa es la única forma de probarlanull
. No veo cómonull
deberí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 tratarnull
s 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 laStream
API también se ven bastante diferentes…Puede utilizar
java.util.Objects.nonNull
para filtrar la lista antes de buscaralgo como
fuente
firstString
serlonull
si el primer elementostrings
esnull
.Optional.of
que no es seguro nulo. Usted podríamap
aOptional.ofNullable
y 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
. ElBinaryOperator
pasado areduce
devuelve ese elemento 1 o"StringWhenListIsEmpty"
si ningún elemento alcanza elreduce
.La belleza de esta solución es que
Optional
no está asignada y laBinaryOperator
lambda 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. Unnull
Foo 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
this
como 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
Box
tipo. ElOptional
tipo en sí puede servir para este propósito. Vea mi respuesta como ejemplo.null
es un valor válido como cualquier otro, sin tratamiento especial para él. (hasta algún tiempo después :)