Por que isso lança um 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
O primeiro item strings
é null
, que é um valor perfeitamente aceitável. Além disso, findFirst()
retorna um Optional , o que faz ainda mais sentido para findFirst()
poder manipular null
s.
EDIT: atualizado orElse()
para ser menos ambíguo.
java
java-8
java-stream
optional
neverendingqs
fonte
fonte
String
aqui, e se for uma lista que representa uma coluna no DB? O valor da primeira linha dessa coluna pode sernull
.null
é um valor perfeitamente aceitável em Java, em geral. Em particular, é um elemento válido para umArrayList<String>
. Como qualquer outro valor, entretanto, há limitações sobre o que pode ser feito com ele. "Nunca usenull
" não é um conselho útil, pois você não pode evitá-lo.findFirst()
, não há mais nada que queira fazer.Respostas:
A razão para isso é o uso de
Optional<T>
na devolução. Opcional não pode conternull
. Essencialmente, ele não oferece nenhuma maneira de distinguir situações "não está lá" e "está lá, mas está definido paranull
".É por isso que a documentação proíbe explicitamente a situação quando
null
é selecionado emfindFirst()
:fonte
Optional
. De qualquer forma, acho que estou quase reclamando - se a linguagem não suporta, não suporta.boolean
para diferenciar essas duas situações faria todo o sentido. Para mim, parece que usarOptional<T>
aqui foi uma escolha questionável.Iterable
tipo, verificahasNext()
e retorna o valor apropriado.findFirst
retornar um valor Opcional vazio no caso de POConforme já discutido , os designers de API não presumem que o desenvolvedor deseja tratar
null
valores e valores ausentes da mesma maneira.Se ainda quiser fazer isso, você pode fazê-lo explicitamente aplicando a sequência
para o riacho. O resultado será um opcional vazio em ambos os casos, se não houver um primeiro elemento ou se o primeiro elemento for
null
. Então, no seu caso, você pode usarString firstString = strings.stream() .map(Optional::ofNullable).findFirst().flatMap(Function.identity()) .orElse(null);
para obter um
null
valor se o primeiro elemento estiver ausente ounull
.Se você quiser distinguir entre esses casos, pode simplesmente omitir a
flatMap
etapa: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"));
Isso não é muito diferente da sua pergunta atualizada. Você apenas tem que substituir
"no such element"
por"StringWhenListIsEmpty"
e"first element is null"
pornull
. Mas se você não gosta de condicionais, também pode obtê-lo como:String firstString = strings.stream().skip(0) .map(Optional::ofNullable).findFirst() .orElseGet(()->Optional.of("StringWhenListIsEmpty")) .orElse(null);
Agora,
firstString
seránull
se um elemento existe, mas énull
e será"StringWhenListIsEmpty"
quando nenhum elemento existir.fonte
null
para 1) o primeiro elemento énull
ou 2) nenhum elemento existe dentro da lista. Eu atualizei a pergunta para remover a ambigüidade.Optional
pode ser atribuído anull
. ComoOptional
é suposto ser um "tipo de valor", nunca deve ser nulo. E um opcional nunca deve ser comparado por==
. O código pode falhar no Java 10 :) ou sempre que o tipo de valor for introduzido no Java.null
e, embora as instâncias nunca devam ser comparadas por==
, a referência pode ser testada paranull
uso,==
pois é a única maneira de testá-lanull
. Não consigo ver como essa transição para “nuncanull
” deve funcionar para o código existente, pois é até o valor padrão para todas as variáveis de instância e elementos de arraynull
. O snippet certamente não é o melhor código, mas também não é a tarefa de tratarnull
s como valores presentes.null
. No entanto, uma vez que tal mudança hipotética de linguagem faria com que o compilador emitisse um erro aqui (não quebrar o código silenciosamente), posso aceitar o fato de que possivelmente teria que ser adaptado para Java 10. Suponho que aStream
API irá parece bem diferente então ...Você pode usar
java.util.Objects.nonNull
para filtrar a lista antes de encontraralgo como
fonte
firstString
sernull
se o primeiro itemstrings
fornull
.Optional.of
que não é seguro nulo. Você poderiamap
paraOptional.ofNullable
e, em seguida, usarfindFirst
, mas você vai acabar com um opcional de OpcionalO texto seguinte substitui código
findFirst()
comlimit(1)
e substituiorElse()
comreduce()
:String firstString = strings. stream(). limit(1). reduce("StringWhenListIsEmpty", (first, second) -> second);
limit()
permite que apenas 1 elemento alcancereduce
. OBinaryOperator
passado parareduce
retorna aquele 1 elemento ou então,"StringWhenListIsEmpty"
se nenhum elemento alcançar oreduce
.A beleza dessa solução é que
Optional
não está alocado e oBinaryOperator
lambda não vai alocar nada.fonte
Opcional deve ser um tipo de "valor". (leia as letras miúdas em javadoc :) JVM poderia até mesmo substituir tudo
Optional<Foo>
por apenasFoo
, removendo todos os custos de encaixotamento e desembalagem. Umnull
Foo significa um vazioOptional<Foo>
.É um design possível para permitir Optional com valor nulo, sem adicionar um sinalizador booleano - basta adicionar um objeto sentinela. (pode até ser usado
this
como sentinela; consulte Throwable.cause)A decisão de que Opcional não pode agrupar nulo não é baseada no custo de tempo de execução. Este foi um problema muito discutido e você precisa cavar as listas de discussão. A decisão não convence a todos.
Em qualquer caso, uma vez que Optional não pode envolver um valor nulo, ele nos empurra para um canto em casos como
findFirst
. Eles devem ter raciocinado que os valores nulos são muito raros (foi até considerado que o Stream deveria barrar os valores nulos), portanto, é mais conveniente lançar uma exceção em valores nulos em vez de em fluxos vazios.Uma solução alternativa é encaixotar
null
, por exemploclass Box<T> static Box<T> of(T value){ .. } Optional<Box<String>> first = stream.map(Box::of).findFirst();
(Eles dizem que a solução para cada problema OOP é introduzir outro tipo :)
fonte
Box
tipo. OOptional
próprio tipo pode servir a esse propósito. Veja minha resposta para um exemplo.null
é um valor válido como qualquer outro, sem tratamento especial para ele. (até algum tempo depois :)