Recentemente, mudamos para o Java 8. Agora, vejo aplicativos inundados de Optional
objetos.
Antes do Java 8 (estilo 1)
Employee employee = employeeServive.getEmployee();
if(employee!=null){
System.out.println(employee.getId());
}
Após o Java 8 (estilo 2)
Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
Employee employee = employeeOptional.get();
System.out.println(employee.getId());
}
Não vejo valor agregado Optional<Employee> employeeOptional = employeeService.getEmployee();
quando o serviço em si retorna opcional:
Vindo de um background Java 6, vejo o Estilo 1 como mais claro e com menos linhas de código. Existe alguma vantagem real que estou perdendo aqui?
Consolidado da compreensão de todas as respostas e de mais pesquisas no blog
employeeOptional.isPresent()
parece estar totalmente ausente do ponto de tipos de opção. De acordo com o comentário de @ MikePartridge, isso não é recomendado ou idiomático. Verificar explicitamente se um tipo de opção é algo ou nada é um não-não. Você deveria simplesmentemap
ouflatMap
sobre eles.Optional
de Brian Goetz , arquiteto de linguagem Java da Oracle.Respostas:
O estilo 2 não usa o Java 8 o suficiente para ver todos os benefícios. Você não quer
if ... use
nada disso. Veja os exemplos da Oracle . Seguindo seus conselhos, obtemos:Estilo 3
Ou um trecho mais longo
fonte
void ifPresent(Consumer<T>, Runnable)
eu diria para uma proibição total deisPresent
eget
ifPresentOrElse(Consumer<? super T>, Runnable)
.Se você estiver usando
Optional
como uma camada de "compatibilidade" entre uma API mais antiga que ainda pode retornarnull
, pode ser útil criar o opcional (não vazio) no estágio mais recente que você tem certeza de que possui alguma coisa. Por exemplo, onde você escreveu:Eu optaria por:
O ponto é que você sabe que existe um serviço de funcionário não nulo , para que você possa encerrar isso
Optional
com umOptional.of()
. Então, quando você ligargetEmployee()
para isso, poderá ou não conseguir um funcionário. Esse funcionário pode (ou, possivelmente, pode não ter) um ID. Então, se você acabou com um ID, deseja imprimi-lo.Não há necessidade de verificar explicitamente qualquer nulo, presença, etc., neste código.
fonte
(...).ifPresent(aMethod)
maisif((...).isPresent()) { aMethod(...); }
? Parece ser apenas mais uma sintaxe para fazer quase a mesma operação.Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet);
Agora eu posso processar de maneirachildren
uniforme, por exemplochildren.forEach(relative::sendGift);
,. Aqueles poderia mesmo ser combinados:Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet).forEach(relative::sendGift);
. Todo o clichê de verificação de null, etc., ...Quase não há valor agregado em ter um
Optional
valor único. Como você viu, isso apenas substitui um cheque pornull
um cheque pela presença.Há um enorme valor agregado em ter uma
Collection
dessas coisas. Todos os métodos de streaming introduzidos para substituir os loops de baixo nível no estilo antigo entendem asOptional
coisas e fazem a coisa certa sobre elas (não processando-as ou retornando outra não configuradaOptional
). Se você lida com n itens com mais frequência do que com itens individuais (e 99% de todos os programas realistas o fazem), é uma grande melhoria ter suporte interno para itens opcionalmente presentes. No caso ideal, você nunca precisa verificarnull
ou presença.fonte
Employee getEmployee()
vsOptional<Employee> getEmployee()
. Embora tecnicamente ambos possam retornarnull
, é muito mais provável que um deles cause problemas..map()
sem ter que procurar por nulos o tempo todo, é ótimo. Por exemploOptional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);
,.null
.Desde que você use
Optional
exatamente como uma API sofisticadaisNotNull()
, sim, você não encontrará diferenças apenas com a verificaçãonull
.Coisas que você não deve usar
Optional
paraUsar
Optional
apenas para verificar a presença de um valor é Bad Code ™:Coisas que você deve usar
Optional
paraEvite que um valor não esteja presente, produzindo um quando necessário ao usar métodos de API de retorno nulo:
Ou, se a criação de um novo funcionário for muito cara:
Além disso, informando a todos que seus métodos podem não retornar um valor (se, por qualquer motivo, você não puder retornar um valor padrão como acima):
E, finalmente, existem todos os métodos de fluxo que já foram explicados em outras respostas, embora não seja realmente você que decida usar,
Optional
mas faça uso doOptional
fornecido por outra pessoa.fonte
Optional
uma oportunidade perdida. "Aqui, peça a novidadeOptional
que fiz para que você seja forçado a verificar valores nulos. Ah, a propósito ... eu adicionei umget()
método a ele para que você não precise realmente verificar nada;);)" . (...)Optional
é legal como um sinal de néon "ISSO PODE SER NULO", mas a existência simples de seuget()
método o prejudica » . Mas a OP estava pedindo razões para usarOptional
, não razões contra ou projetar falhas.Employee employee = Optional.ofNullable(employeeService.getEmployee());
No seu terceiro snippet, o tipo não deveria serOptional<Employee>
?get
é se você precisar interagir com algum código legado. Não consigo pensar em muitos motivos válidos no novo código para usarget
. Dito isto, ao interagir com o código legado, muitas vezes não há alternativa, mas ainda assim walen tem o argumento de que a existênciaget
faz com que os iniciantes escrevam códigos ruins.Map
uma vez e não estou ciente de alguém fazer uma alteração tão drástica e não compatível com versões anteriores, para ter certeza de que você poderia projetar intencionalmente APIs ruins comOptional
- não sei o que isso prova. UmOptional
faria sentido para algumtryGet
método embora.Map
é um exemplo com o qual todos estão familiarizados. É claro que eles não podem tornar oMap.get
retorno um Opcional por causa da compatibilidade com versões anteriores, mas você pode facilmente imaginar outra classe do tipo Mapa que não teria essas restrições de compatibilidade. Digaclass UserRepository {Optional<User> getUserDetails(int userID) {...}}
. Agora você deseja obter os detalhes do usuário conectado e sabe que eles existem porque eles conseguiram efetuar login e seu sistema nunca exclui usuários. Então você faztheUserRepository.getUserDetails(loggedInUserID).get()
.O ponto chave para mim ao usar o Opcional é manter claro quando o desenvolvedor precisa verificar se a função retorna valor ou não.
fonte
Seu principal erro é que você ainda está pensando em termos processuais. Isso não é uma crítica a você como pessoa, é apenas uma observação. Pensar em termos mais funcionais vem com o tempo e a prática e, portanto, os métodos são apresentados e parecem as coisas corretas mais óbvias para você. Seu erro secundário secundário é criar o seu Opcional dentro do seu método. O Opcional tem como objetivo ajudar a documentar que algo pode ou não retornar um valor. Você pode não conseguir nada.
Isso fez com que você escrevesse um código perfeitamente legível que parecesse perfeitamente razoável, mas você foi seduzido pelas vil sedutoras sedutoras que são get e isPresent.
É claro que a pergunta rapidamente se torna "por que o isPresent está chegando lá?"
Uma coisa que muitas pessoas aqui sentem falta é isPresent (), que não é algo que é feito para um novo código escrito por pessoas totalmente a bordo com o quão bom é o lambda útil e quem gosta da coisa funcional.
No entanto, oferece alguns (dois) benefícios bons, ótimos e charmosos (?):
O primeiro é bastante simples.
Imagine que você tenha uma API parecida com esta:
E você tinha uma classe herdada usando isso como tal (preencha os espaços em branco):
Um exemplo inventado para ter certeza. Mas tenha paciência comigo aqui.
O Java 8 já foi lançado e estamos nos esforçando para entrar a bordo. Portanto, uma das coisas que fazemos é que queremos substituir nossa interface antiga por algo que retorne Opcional. Por quê? Porque, como alguém já mencionou graciosamente: Isso elimina a suposição de se algo pode ou não ser nulo. Isso já foi apontado por outros. Mas agora temos um problema. Imagine que temos (desculpe-me enquanto pressiono alt + F7 em um método inocente), 46 lugares em que esse método é chamado em código legado bem testado que, caso contrário, faz um excelente trabalho. Agora você precisa atualizar tudo isso.
É aqui que está presente o brilho.
Porque agora: Snickers lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {lança novo NoSuchSnickersException (); } else {consumer.giveDiabetes (lastSnickers); }
torna-se:
E essa é uma mudança simples que você pode dar ao novo junior: ele pode fazer algo útil e poderá explorar a base de código ao mesmo tempo. ganha-ganha. Afinal, algo semelhante a esse padrão é bastante difundido. E agora você não precisa reescrever o código para usar lambdas ou qualquer coisa. (Nesse caso em particular, seria trivial, mas deixo pensando em exemplos em que isso seria difícil como exercício para o leitor.)
Observe que isso significa que a maneira como você fez isso é essencialmente uma maneira de lidar com o código herdado sem fazer reescritas caras. E o novo código?
Bem, no seu caso, onde você apenas deseja imprimir algo, basta:
snickersCounter.lastConsumedSnickers (). ifPresent (System.out :: println);
O que é bastante simples e perfeitamente claro. O ponto que está subindo lentamente à superfície é que existem casos de uso para get () e isPresent (). Eles estão lá para permitir que você modifique o código existente mecanicamente para usar os tipos mais recentes sem pensar muito nisso. O que você está fazendo é, portanto, equivocado das seguintes maneiras:
Se você deseja usar o Opcional como uma verificação simples de segurança nula, o que você deveria ter feito é simplesmente o seguinte:
Obviamente, a versão mais bonita disso se parece com:
A propósito, enquanto isso não é de forma alguma necessário, recomendo o uso de uma nova linha por operação, para facilitar a leitura. Fácil de ler e entender supera a concisão em qualquer dia da semana.
É claro que este é um exemplo muito simples, onde é fácil entender tudo o que estamos tentando fazer. Nem sempre é tão simples na vida real. Mas observe como, neste exemplo, o que estamos expressando são nossas intenções. Queremos OBTER o funcionário, OBTER seu ID e, se possível, imprimi-lo. Esta é a segunda grande vitória do Opcional. Isso nos permite criar um código mais claro. Eu também acho que fazer coisas como criar um método que faça um monte de coisas para que você possa alimentá-lo em um mapa é geralmente uma boa idéia.
fonte
map(x).ifPresent(f)
simplesmente não faz o mesmo tipo de sentido intuitivo queif(o != null) f(x)
faz. Aif
versão é perfeitamente clara. Este é outro caso de pessoas pulando em um vagão de banda popular, * não * um caso de progresso real.Optional
. Eu estava me referindo apenas ao uso de opcional neste caso específico, e mais ainda à legibilidade, pois várias pessoas estão agindo como esse formato é igual ou mais legível do queif
.isPresent
eget
o máximo possível) melhorar a segurança . É mais difícil escrever código incorreto que falha ao verificar a ausência de um valor se o tipo é emOptional<Whatever>
vez de usar um valor nuloWhatever
.get
, será forçado a escolher o que fazer quando ocorrer um valor ausente: você usariaorElse()
(ouorElseGet()
) para fornecer um padrão ouorElseThrow()
lançar um valor escolhido. exceção que documenta a natureza do problema , que é melhor que um NPE genérico que não faz sentido até você cavar o rastreamento de pilha.Não vejo benefício no Estilo 2. Você ainda precisa perceber que precisa da verificação nula e agora é ainda maior, portanto menos legível.
Melhor estilo seria, se employeeServive.getEmployee () retornasse Opcional e o código se tornasse:
Dessa forma, você não pode chamar callemployeeServive.getEmployee (). GetId () e perceberá que deve lidar com valores opcionais de alguma forma. E se em todos os métodos de sua base de código nunca retornar nulo (ou quase nunca com exceções conhecidas por sua equipe a esta regra), isso aumentará a segurança contra o NPE.
fonte
isPresent()
. Usamos opcional para não precisarmos testar.isPresent()
. Essa é a abordagem incorreta dos opcionais. Usemap
ou emflatMap
vez disso.ifPresent
) é apenas outra maneira de testar.ifPresent(x)
é tanto um teste quantoif(present) {x}
. O valor de retorno é irrelevante.Eu acho que o maior benefício é que, se a assinatura do método retornar um,
Employee
você não procurará nulo. Você sabe por essa assinatura que é garantido retornar um funcionário. Você não precisa lidar com um caso de falha. Com um opcional, você sabe que sim.No código que eu vi, há verificações nulas que nunca falham, pois as pessoas não desejam rastrear o código para determinar se um nulo é possível; portanto, elas lançam verificações nulas em qualquer lugar defensivamente. Isso torna o código um pouco mais lento, mas, mais importante, torna o código muito mais barulhento.
No entanto, para que isso funcione, você precisa aplicar esse padrão de forma consistente.
fonte
@Nullable
e em@ReturnValuesAreNonnullByDefault
conjunto com a análise estática.package-info.java
com@ReturnValuesAreNonnullByDefault
a todos os meus pacotes, então tudo o que tem a fazer é adicionar@Nullable
onde você tem que mudar paraOptional
. Isso significa menos caracteres, sem adição de lixo e sem sobrecarga de tempo de execução. Não preciso procurar a documentação, como mostra, ao passar o mouse sobre o método. A ferramenta de análise estática detecta erros e, posteriormente, altera a nulidade.Optional
é que ela adicione uma maneira alternativa de lidar com a nulidade. Se estava em Java desde o início, pode ser bom, mas agora é apenas uma dor. Seguir o caminho Kotlin seria IMHO muito melhor. +++ Observe queList<@Nullable String>
ainda é uma lista de strings, enquantoList<Optional<String>>
não é.Minha resposta é: simplesmente não. Pelo menos pense duas vezes sobre se é realmente uma melhoria.
Uma expressão (retirada de outra resposta) como
parece ser a maneira funcional correta de lidar com métodos de retorno originalmente anuláveis. Parece bom, nunca joga e nunca faz você pensar em lidar com o caso "ausente".
Parece melhor do que o estilo antigo
o que torna óbvio que pode haver
else
cláusulas.O estilo funcional
orElse
nem sempre é suficiente)Em nenhum caso, deve ser usado como uma panacéia. Se fosse universalmente aplicável, a semântica do
.
operador deve ser substituída pela semântica do?.
operador , o NPE esquecido e todos os problemas ignorados.Embora seja um grande fã de Java, devo dizer que o estilo funcional é apenas um substituto pobre da declaração de um homem Java
bem conhecido de outras línguas. Concordamos que esta declaração
fonte
employeeService.getEmployee().getId().apply(id => System.out.println(id));
e torná-lo nulo seguro uma vez usando o operador de navegação segura e uma vez usando oOptional
. Certamente, podemos chamá-lo de "detalhe da implementação", mas a implementação é importante. Há casos em que as lamdas tornam o código mais simples e agradável, mas aqui é apenas um abuso feio.Optional.of(employeeService).map(EmployeeService::getEmployee).map(Employee::getId).ifPresent(System.out::println);
recurso de linguagem:employeeService.getEmployee()?.getId()?.apply(id => System.out.println(id));
. Duas sintaxes, mas elas acabam parecendo muito comigo. E o "conceito" éOptional
akaNullable
akaMaybe
+++
O que é abusado são lamdas para lidar com a nulidade. Lamdas são boas, masOptional
não são. A nulidade simplesmente precisa de um suporte de idioma adequado, como no Kotlin. Outro problema:List<Optional<String>>
não está relacionadoList<String>
, onde precisaríamos que eles estivessem relacionados.Opcional é um conceito (um tipo de ordem superior) proveniente da programação funcional. Usá-lo acaba com a verificação nula aninhada e salva você da pirâmide da destruição.
fonte