Tendo usado o Java 8 agora há mais de 6 meses, estou muito feliz com as novas alterações na API. Uma área em que ainda não estou confiante é quando usar Optional
. Eu pareço variar entre querer usá-lo em qualquer lugar em que algo possa estar null
, e em lugar nenhum.
Parece haver muitas situações em que eu poderia usá-lo, e nunca tenho certeza se isso traz benefícios (legibilidade / segurança nula) ou apenas causa sobrecarga adicional.
Portanto, tenho alguns exemplos e estaria interessado nos pensamentos da comunidade sobre se isso Optional
é benéfico.
1 - Como método de retorno público, digite quando o método poderia retornar null
:
public Optional<Foo> findFoo(String id);
2 - Como parâmetro de método quando o parâmetro pode ser null
:
public Foo doSomething(String id, Optional<Bar> barOptional);
3 - Como membro opcional de um bean:
public class Book {
private List<Pages> pages;
private Optional<Index> index;
}
4 - Entrada Collections
:
Em geral, eu não penso:
List<Optional<Foo>>
adiciona qualquer coisa - especialmente porque se pode usar filter()
para remover null
valores, etc., mas há bons usos Optional
nas coleções?
Algum caso que eu perdi?
Map<Character, String>
. Se não há substituição posso usar isto:Optional.ofNullable(map.get(c)).orElse(String.valueOf(c))
. Observe também que o opcional foi roubado da goiaba e possui uma sintaxe muito melhor:Optional.fromNullable(map.get(c)).or(String.valueOf(c));
.filter(Optional::absent)
"valores nulos" sairgetOrDefault()
?Respostas:
O ponto principal de
Optional
é fornecer um meio para uma função retornar um valor para indicar a ausência de um valor de retorno. Veja esta discussão . Isso permite que o chamador continue uma cadeia de chamadas de método fluentes.Isso se aproxima mais do caso de uso nº 1 da pergunta do OP. Embora a ausência de um valor seja uma formulação mais precisa que nula, uma vez que algo como
IntStream.findFirst
nunca poderia retornar nulo.Para o caso de uso # 2 , passando um argumento opcional para um método, isso pode funcionar, mas é um pouco desajeitado. Suponha que você tenha um método que use uma sequência seguida por uma segunda sequência opcional. Aceitar um
Optional
como o segundo argumento resultaria em código como este:Mesmo aceitar nulo é melhor:
Provavelmente, o melhor é ter um método sobrecarregado que aceite um argumento de cadeia única e forneça um padrão para o segundo:
Isso tem limitações, mas é muito melhor do que qualquer um dos itens acima.
Os casos de uso 3 e 4 , com um
Optional
campo de classe ou estrutura de dados, são considerados um uso indevido da API. Primeiro, vai contra o objetivo principal do design,Optional
conforme indicado na parte superior. Segundo, não agrega nenhum valor.Existem três maneiras de lidar com a ausência de um valor em um
Optional
: fornecer um valor substituto, chamar uma função para fornecer um valor substituto ou lançar uma exceção. Se você estiver armazenando em um campo, faça isso no momento da inicialização ou da atribuição. Se você estiver adicionando valores a uma lista, como o OP mencionado, você tem a opção adicional de simplesmente não adicionar o valor, "achatando" os valores ausentes.Tenho certeza de que alguém poderia apresentar alguns casos inventados em que realmente deseja armazenar
Optional
um campo ou uma coleção, mas, em geral, é melhor evitar fazer isso.fonte
Optional
um campo quando as coisas devem ser feitas ou não na presença ou ausência de um valor.if(foo == null)
internamente seria melhor do que apenas armazenar o campo comooptional
. E não vejo por que ligar explicitamentegetOptionalFoo()
internamente é melhor do que apenas armazenar o campo como umOptional
. Além disso, se um campo pode sernull
e um campo não pode sernull
, por que não comunicar isso ao compilador, criando-oOptional
ou nãoOptional
.Optional<>
especialistas: "não guardeOptional<>
em um campo". Estou realmente tentando entender o objetivo desta recomendação. Considere uma árvore DOM em que um nó possa ter um pai ou não. Parece no caso de uso queNode.getParent()
retornariaOptional<Node>
. Devo realmente armazenar umnull
e agrupar o resultado toda vez? Por quê? O que essa ineficiência extra me compra, além de tornar meu código feio?Optional<Node> getParent() { return Optional.ofNullable(parent); }
. Isso parece caro, porque aloca umOptional
toda vez! Mas se o chamador descompactá-lo imediatamente, o objeto é extremamente curto e nunca é promovido fora do espaço eden. Pode até ser eliminado pela análise de escape do JIT. A resposta final é "depende", como sempre, mas o usoOptional
em campos potencialmente incha o uso da memória e diminui a velocidade da travessia da estrutura de dados. E, finalmente, acho que isso atrapalha o código, mas isso é uma questão de gosto.Estou atrasado para o jogo, mas para o que vale a pena, quero adicionar meus 2 centavos. Eles vão contra o objetivo do projeto
Optional
, que é bem resumido pela resposta de Stuart Marks , mas ainda estou convencido de sua validade (obviamente).Use opcional em qualquer lugar
Em geral
Eu escrevi um post
Optional
inteiro sobre o uso, mas basicamente se resume a isso:Optional
vez denull
As duas primeiras exceções podem reduzir a sobrecarga percebida das referências de quebra e descompactação
Optional
. Eles são escolhidos de forma que um nulo nunca possa legalmente passar um limite de uma instância para outra.Observe que isso quase nunca permitirá
Optional
s em coleções, o que é quase tão ruim quantonull
s. Apenas não faça isso. ;)Em relação às suas perguntas
Vantagens
Fazer isso reduz a presença de
null
s na sua base de código, embora não os erradique. Mas esse nem é o ponto principal. Existem outras vantagens importantes:Esclarece a intenção
O uso
Optional
expressa claramente que a variável é, bem, opcional. Qualquer leitor do seu código ou consumidor da sua API será surpreendido com o fato de que talvez não haja nada lá e que uma verificação seja necessária antes de acessar o valor.Remove incerteza
Sem
Optional
o significado de umanull
ocorrência não é claro. Pode ser uma representação legal de um estado (consulteMap.get
) ou um erro de implementação, como uma inicialização ausente ou com falha.Isso muda drasticamente com o uso persistente de
Optional
. Aqui, a ocorrência de jánull
significa a presença de um bug. (Como se o valor estivesse faltando, umOptional
teria sido usado.) Isso torna a depuração de uma exceção de ponteiro nulo muito mais fácil, pois a pergunta do significado dissonull
já está respondida.Mais verificações nulas
Agora que nada pode ser
null
mais, isso pode ser aplicado em qualquer lugar. Seja com anotações, asserções ou verificações simples, você nunca precisa pensar se esse argumento ou esse tipo de retorno pode ser nulo. Não pode!Desvantagens
Claro, não há bala de prata ...
atuação
A quebra de valores (especialmente primitivos) em uma instância extra pode prejudicar o desempenho. Em laços apertados, isso pode se tornar perceptível ou até pior.
Observe que o compilador pode contornar a referência extra para vidas úteis de
Optional
s. No Java 10, os tipos de valor podem reduzir ainda mais ou remover a penalidade.Serialização
Optional
não é serializável, mas uma solução alternativa não é muito complicada.Invariância
Devido à invariância de tipos genéricos em Java, certas operações se tornam complicadas quando o tipo de valor real é empurrado para um argumento de tipo genérico. Um exemplo é dado aqui (consulte "Polimorfismo paramétrico") .
fonte
List
s. Este é o caso quando é importante que um determinado elemento esteja localizado em um determinado índice, mas o elemento pode estar presente ou não. Isso é claramente comunicado por umList<Optional<T>>
tipo. Se, por outro lado, é importante apenas quais objetos são membros de alguma coleção, então não faz sentido.Optional
ser passado para um método pode sernull
. Então, o que você ganhou? Agora você tem duas verificações a fazer. Veja stackoverflow.com/a/31923042/650176Optional
(o que deve ser o caso se você estiver disposto a passar como argumento)null
nunca é um valor legal. Portanto, chamar um método com um parâmetro explícitonull
é obviamente um erro.Pessoalmente, eu prefiro usar a Ferramenta de Inspeção de Código do IntelliJ para usar
@NotNull
e@Nullable
verificações, pois elas são em grande parte tempo de compilação (podem ter algumas verificações em tempo de execução). Não é tão rigoroso quanto usar o Opcional, no entanto, essa falta de rigor deve ser apoiada por testes de unidade decentes.Isso funciona com o Java 5 e não há necessidade de quebrar e desembrulhar valores. (ou crie objetos wrapper)
fonte
@Nonnull
pessoalmente - Trabalha com FindBugs;)@Nonnull @NotNull etc
Eu acho que o Guava Optional e sua página wiki o colocam muito bem:
Optional
acrescenta alguma sobrecarga, mas acho que sua clara vantagem é explicitar que um objeto pode estar ausente e fazer com que os programadores lidem com a situação. Impede que alguém esqueça o!= null
cheque amado .Tomando o exemplo de 2 , acho que é um código muito mais explícito escrever:
do que
Para mim, o
Optional
melhor captura o fato de que não há placa de som presente.Meus 2 ¢ sobre seus pontos:
public Optional<Foo> findFoo(String id);
- Eu não tenho certeza disso. Talvez eu devesse retornar umResult<Foo>
que possa estar vazio ou conter umFoo
. É um conceito semelhante, mas não é realmente umOptional
.public Foo doSomething(String id, Optional<Bar> barOptional);
- Eu preferiria o @Nullable e um findbugs, como na resposta de Peter Lawrey - veja também esta discussão .Optional<Index> getIndex()
para indicar explicitamente que o livro pode não ter um índice.Em geral, eu tentaria minimizar a passagem de
null
s. (Uma vez queimado ...) Acho que vale a pena encontrar as abstrações apropriadas e indicar aos colegas programadores o que realmente representa um certo valor de retorno.fonte
soundcard.ifPresent(System.out::println)
. ChamarisPresent
é semanticamente o mesmo que procurarnull
.if(soundcard != null && soundcard.isPresent())
. Embora fazer uma API que retorne um Opcional e também possa retornar nulo seja algo que espero que ninguém faça.No tutorial do Oracle :
fonte
Aqui está um bom artigo que mostra a utilidade do caso de usuário nº 1. Existe esse código
é transformado para isso
usando Opcional como um valor de retorno dos respectivos métodos getter .
fonte
Aqui está um uso interessante (acredito) para ... Testes.
Pretendo testar fortemente um de meus projetos e, portanto, construo asserções; só há coisas que tenho que verificar e outras não.
Portanto, construo coisas para afirmar e uso uma declaração para verificá-las, assim:
Na classe NodeAssert, faço isso:
O que significa que a declaração realmente só é acionada se eu quiser verificar o rótulo!
fonte
Optional
A classe permite evitar o usonull
e fornecer uma alternativa melhor:Isso incentiva o desenvolvedor a verificar a presença, a fim de evitar as não capturadas
NullPointerException
.A API fica melhor documentada porque é possível ver onde esperar os valores que podem estar ausentes.
Optional
fornece API conveniente para trabalhos futuros com o objetoisPresent()
:;get()
;orElse()
;orElseGet()
;orElseThrow()
;map()
;filter()
;flatmap()
.Além disso, muitas estruturas usam ativamente esse tipo de dados e o retornam da API.
fonte
Em java, apenas não os use, a menos que você seja viciado em programação funcional.
Eles não têm lugar como argumentos de método (eu prometo que alguém um dia passará para você um opcional nulo, não apenas um opcional que está vazio).
Eles fazem sentido para os valores de retorno, mas convidam a classe cliente a continuar esticando a cadeia de construção de comportamento.
FP e cadeias têm pouco espaço em uma linguagem imperativa como o java, porque torna muito difícil depurar, não apenas ler. Quando você pisa na linha, não pode conhecer o estado nem a intenção do programa; você precisa descobrir isso (em código que geralmente não é seu e muitos quadros de pilha com profundidade, apesar dos filtros de etapas) e precisa adicionar muitos pontos de interrupção para garantir que ele pare no código / lambda que você adicionou, em vez de simplesmente caminhar pelas linhas triviais if / else /.
Se você deseja programação funcional, escolha algo além de java e espere ter as ferramentas para depurar isso.
fonte
Eu não acho que opcional seja um substituto geral para métodos que potencialmente retornam valores nulos.
A idéia básica é: A ausência de um valor não significa que ele esteja potencialmente disponível no futuro. É uma diferença entre findById (-1) e findById (67).
A principal informação dos opcionais para o chamador é que ele pode não contar com o valor fornecido, mas pode estar disponível em algum momento. Talvez ele desapareça novamente e volte mais tarde mais uma vez. É como um interruptor de ligar / desligar. Você tem a "opção" de ligar ou desligar a luz. Mas você não tem opção se não tiver uma luz para acender.
Por isso, acho muito confuso introduzir Opcionais em todos os lugares onde anteriormente nulo era potencialmente retornado. Ainda utilizarei nulo, mas apenas em áreas restritas, como a raiz de uma árvore, inicialização lenta e métodos explícitos de localização.
fonte
Parece
Optional
só é útil se o tipo T em Opcional é um tipo primitivo, comoint
,long
,char
, etc. Para as classes "reais", não faz sentido para mim como você pode usar umnull
valor de qualquer maneira.Eu acho que foi tirado daqui (ou de outro conceito de linguagem similar).
Nullable<T>
Em C #, isso
Nullable<T>
foi introduzido há muito tempo para agrupar tipos de valor.fonte
Optional
verdade, é uma imitação da goiabajava.util.Optional
.Um possui semântica semelhante a uma instância não modificável do padrão de design do Iterator :
Optional
isPresent()
)get()
) se se referir a um objetonext()
método).Portanto, considere retornar ou passar um
Optional
em contextos nos quais você poderia ter considerado anteriormente usar um JavaIterator
.fonte
O Java SE 8 apresenta uma nova classe chamada java.util.Optional,
Você pode criar um opcional ou opcional vazio com valor nulo.
E aqui está um opcional com um valor não nulo:
Faça algo se um valor estiver presente
Agora que você possui um objeto Opcional, pode acessar os métodos disponíveis para lidar explicitamente com a presença ou ausência de valores. Em vez de ter que se lembrar de fazer uma verificação nula, da seguinte maneira:
Você pode usar o método ifPresent (), da seguinte maneira:
fonte