Por que usar Opcional no Java 8+ em vez das verificações tradicionais de ponteiro nulo?


110

Recentemente, mudamos para o Java 8. Agora, vejo aplicativos inundados de Optionalobjetos.

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



28
Que nojo! 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 simplesmente mapou flatMapsobre eles.
Andrés F.

7
Veja uma resposta de estouro de pilhaOptional de Brian Goetz , arquiteto de linguagem Java da Oracle.
Basil Bourque

6
É o que acontece quando você desliga o cérebro em nome das "melhores práticas".
immibis

9
Esse é um uso bastante ruim do opcional, mas mesmo isso traz benefícios. Com valores nulos tudo pode ser nulo, então você precisa ser constantemente verificando, chamadas opcionais que esta variável particular é susceptível de ser ausente, certifique-se de verificar para ele
Richard Tingle

Respostas:


108

O estilo 2 não usa o Java 8 o suficiente para ver todos os benefícios. Você não quer if ... usenada disso. Veja os exemplos da Oracle . Seguindo seus conselhos, obtemos:

Estilo 3

// Changed EmployeeServive to return an optional, no more nulls!
Optional<Employee> employee = employeeServive.getEmployee();
employee.ifPresent(e -> System.out.println(e.getId()));

Ou um trecho mais longo

Optional<Employee> employee = employeeServive.getEmployee();
// Sometimes an Employee has forgotten to write an up-to-date timesheet
Optional<Timesheet> timesheet = employee.flatMap(Employee::askForCurrentTimesheet); 
// We don't want to do the heavyweight action of creating a new estimate if it will just be discarded
client.bill(timesheet.orElseGet(EstimatedTimesheet::new));

19
de fato, banimos a maioria dos usos de isPresent (capturado pela revisão de código)
jk.

10
@jk. de fato, se houvesse uma sobrecarga void ifPresent(Consumer<T>, Runnable)eu diria para uma proibição total de isPresenteget
Caleth

10
@Caleth: Você pode estar interessado em Java 9's ifPresentOrElse(Consumer<? super T>, Runnable).
Wchargin

9
Eu acho uma desvantagem nesse estilo java 8, comparado ao java 6: engana as ferramentas de cobertura de código. Com a instrução if, ele mostra quando você perdeu uma ramificação, não aqui.
Thierry

14
@Aaron "reduz drasticamente a legibilidade do código". Que parte reduz exatamente a legibilidade? Parece mais um "eu sempre fiz de maneira diferente e não quero mudar meus hábitos" do que um raciocínio objetivo.
Voo

47

Se você estiver usando Optionalcomo uma camada de "compatibilidade" entre uma API mais antiga que ainda pode retornar null, 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:

Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

Eu optaria por:

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

O ponto é que você sabe que existe um serviço de funcionário não nulo , para que você possa encerrar isso Optionalcom um Optional.of(). Então, quando você ligar getEmployee()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.


4
Qual é a vantagem de usar (...).ifPresent(aMethod)mais if((...).isPresent()) { aMethod(...); }? Parece ser apenas mais uma sintaxe para fazer quase a mesma operação.
Ruslan

2
@Ruslan Por que usar uma sintaxe especial para uma chamada específica quando você já possui uma solução geral que funciona igualmente bem em todos os casos? Também estamos aplicando a mesma ideia do mapa e da empresa aqui.
Voo

1
@Ruslan ... uma API retorna nulos em vez de coleções ou matrizes vazias. Por exemplo, você pode fazer algo como (assumindo uma classe Parent para a qual getChildren () retorne um conjunto de filhos ou null se não houver filhos): Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet); Agora eu posso processar de maneira childrenuniforme, por exemplo children.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., ...
Joshua Taylor

1
@Ruslan ... é delegado ao código testado e verdadeiro na biblioteca padrão. Isso reduz a quantidade de código que eu, como programador, tenho que escrever, testar, depurar etc.
Joshua Taylor

1
Ainda bem que estou usando o Swift. se deixe empregado = employeeService.employee {print (employee.Id); }
gnasher729 20/0118

23

Quase não há valor agregado em ter um Optionalvalor único. Como você viu, isso apenas substitui um cheque por nullum cheque pela presença.

Há um enorme valor agregado em ter uma Collectiondessas coisas. Todos os métodos de streaming introduzidos para substituir os loops de baixo nível no estilo antigo entendem as Optionalcoisas e fazem a coisa certa sobre elas (não processando-as ou retornando outra não configurada Optional). 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 verificar null ou presença.


24
Além de coleções / riachos, um opcional também é ótimo para fazer APIs mais auto-documentação, por exemplo, Employee getEmployee()vs Optional<Employee> getEmployee(). Embora tecnicamente ambos possam retornar null, é muito mais provável que um deles cause problemas.
18718 amon

16
Há muito valor em um opcional de um único valor. Ser capaz de, .map()sem ter que procurar por nulos o tempo todo, é ótimo. Por exemplo Optional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);,.
18718 Joshua Taylor

9
Discordo do seu comentário inicial sem valor agregado. Opcional fornece contexto ao seu código. Uma rápida olhada pode indicar a correção da função e todos os casos são cobertos. Considerando que, com verificação nula, você deve verificar nula todos os tipos de referência em todos os métodos? Um caso semelhante pode ser aplicado a Classes e modificadores públicos / privados; eles agregam valor zero ao seu código, certo?
precisa saber é

3
@ JoshuaTaylor Honestamente, comparado a essa expressão, prefiro o cheque à moda antiga null.
Kilian Foth

2
Outra grande vantagem do Opcional, mesmo com valores únicos, é que você não pode esquecer de procurar nulo quando deve fazê-lo. Caso contrário, é muito fácil esquecer o cheque e acabar com um NullPointerException ...
Sean Burton

17

Desde que você use Optionalexatamente como uma API sofisticada isNotNull(), sim, você não encontrará diferenças apenas com a verificação null.

Coisas que você não deve usar Optionalpara

Usar Optionalapenas para verificar a presença de um valor é Bad Code ™:

// BAD CODE ™ --  just check getEmployee() != null  
Optional<Employee> employeeOptional =  Optional.ofNullable(employeeService.getEmployee());  
if(employeeOptional.isPresent()) {  
    Employee employee = employeeOptional.get();  
    System.out.println(employee.getId());
}  

Coisas que você deve usar Optionalpara

Evite que um valor não esteja presente, produzindo um quando necessário ao usar métodos de API de retorno nulo:

Employee employee = Optional.ofNullable(employeeService.getEmployee())
                            .orElseGet(Employee::new);
System.out.println(employee.getId());

Ou, se a criação de um novo funcionário for muito cara:

Optional<Employee> employee = Optional.ofNullable(employeeService.getEmployee());
System.out.println(employee.map(Employee::getId).orElse("No employee found"));

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):

// Your code without Optional
public Employee getEmployee() {
    return someCondition ? null : someEmployee;
}
// Someone else's code
Employee employee = getEmployee(); // compiler doesn't complain
// employee.get...() -> NPE awaiting to happen, devs criticizing your code

// Your code with Optional
public Optional<Employee> getEmployee() {
    return someCondition ? Optional.empty() : Optional.of(someEmployee);
}
// Someone else's code
Employee employee = getEmployee(); // compiler complains about incompatible types
// Now devs either declare Optional<Employee>, or just do employee = getEmployee().get(), but do so consciously -- not your fault.

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, Optionalmas faça uso do Optionalfornecido por outra pessoa.


1
@Kapep De fato. Tivemos esse debate em / r / java e eu disse : «Vejo Optionaluma oportunidade perdida. "Aqui, peça a novidade Optionalque fiz para que você seja forçado a verificar valores nulos. Ah, a propósito ... eu adicionei um get()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 seu get()método o prejudica » . Mas a OP estava pedindo razões para usar Optional, não razões contra ou projetar falhas.
walen

1
Employee employee = Optional.ofNullable(employeeService.getEmployee());No seu terceiro snippet, o tipo não deveria ser Optional<Employee>?
Charlie Harding

2
@immibis De que maneira aleijado? A principal razão para usar geté se você precisar interagir com algum código legado. Não consigo pensar em muitos motivos válidos no novo código para usar get. 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ência getfaz com que os iniciantes escrevam códigos ruins.
Voo

1
@immibis Eu não mencionei Mapuma 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 com Optional- não sei o que isso prova. Um Optionalfaria sentido para algum tryGetmétodo embora.
Voo 25/01

2
@Voo Mapé um exemplo com o qual todos estão familiarizados. É claro que eles não podem tornar o Map.getretorno 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. Diga class 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ê faz theUserRepository.getUserDetails(loggedInUserID).get().
immibis

12

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.

//service 1
Optional<Employee> employeeOptional = employeeService.getEmployee();
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

//service 2
Employee employee = employeeService.getEmployeeXTPO();
System.out.println(employee.getId());

8
Me surpreende que todas as coisas funcionais bacanas com opcionais, embora sejam realmente boas de se ter, sejam consideradas mais importantes que esse ponto aqui. Antes do java 8, era necessário procurar no Javadoc para saber se era necessário checar a resposta de uma chamada. Poste java 8, você sabe pelo tipo de retorno se pode obter "nada" de volta ou não.
Buhb

3
Bem, há o problema do "código antigo" em que as coisas ainda podem retornar nulas ... mas sim, ser capaz de distinguir da assinatura é ótimo.
Haakon Løtveit 19/01/19

5
Sim, certamente funciona melhor em idiomas onde opcional <T> é a única maneira de expressar que um valor pode estar ausente, ou seja, idiomas sem ptr nulo.
dureuill

8

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 (?):

  • Facilita a transição do código legado para usar os novos recursos.
  • Facilita as curvas de aprendizado do Opcional.

O primeiro é bastante simples.

Imagine que você tenha uma API parecida com esta:

public interface SnickersCounter {
  /** 
   * Provides a proper count of how many snickers have been consumed in total.
   */
  public SnickersCount howManySnickersHaveBeenEaten();

  /**
    * returns the last snickers eaten.<br>
    * If no snickers have been eaten null is returned for contrived reasons.
    */
  public Snickers lastConsumedSnickers();
}

E você tinha uma classe herdada usando isso como tal (preencha os espaços em branco):

Snickers lastSnickers = snickersCounter.lastConsumedSnickers();
if(null == lastSnickers) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers);
}

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:

Optional<Snickers> lastSnickers = snickersCounter.lastConsumedSnickers();
if(!lastSnickers.isPresent()) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers.get());
}

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:

  • Você está chamando um método que pode retornar nulo. A idéia correta seria que o método retorne nulo.
  • Você está usando os métodos bandaid herdados para lidar com isso opcional, em vez de usar os novos métodos saborosos que contêm fantasia lambda.

Se você deseja usar o Opcional como uma verificação simples de segurança nula, o que você deveria ter feito é simplesmente o seguinte:

new Optional.ofNullable(employeeServive.getEmployee())
    .map(Employee::getId)
    .ifPresent(System.out::println);

Obviamente, a versão mais bonita disso se parece com:

employeeService.getEmployee()
    .map(Employee::getId)
    .ifPresent(System.out::println);

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.


1
O problema aqui é que você tenta fazer parecer que essas versões funcionais são tão legíveis quanto as versões mais antigas; em um caso, mesmo dizendo que um exemplo era "perfeitamente claro". Esse não é o caso. Esse exemplo foi claro, mas não perfeitamente , pois requer mais reflexão. map(x).ifPresent(f)simplesmente não faz o mesmo tipo de sentido intuitivo que if(o != null) f(x)faz. A ifversão é perfeitamente clara. Este é outro caso de pessoas pulando em um vagão de banda popular, * não * um caso de progresso real.
Aaron

1
Observe que não estou dizendo que não há benefício zero no uso de programação funcional ou de 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 que if.
Aaron

1
Depende de nossas noções de clareza. Enquanto você faz pontos muito bons, gostaria também de salientar que, para muitas pessoas, as lambdas eram novas e estranhas em algum momento. Ousaria apostar em um cookie da Internet que muitas pessoas dizem ser presente e obter um alívio agradável: agora eles podiam continuar atualizando o código legado e aprender a sintaxe lambda mais tarde. Por outro lado, as pessoas com Lisp, Haskell ou experiência linguagem similar eram provavelmente muito contente que eles agora poderiam aplicar padrões familiares para os seus problemas em java ...
Haakon Løtveit

@ Aaron - clareza não é o ponto dos tipos opcionais. Pessoalmente, acho que eles não diminuem a clareza e, em alguns casos (embora não seja o caso), eles aumentam, mas o principal benefício é que (desde que você evite usar isPresente geto 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 é em Optional<Whatever>vez de usar um valor nulo Whatever.
Jules

Mesmo se você retirar imediatamente os valores, contanto que não o useget , será forçado a escolher o que fazer quando ocorrer um valor ausente: você usaria orElse()(ou orElseGet()) para fornecer um padrão ou orElseThrow()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.
Jules

0

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:

Optional<Employee> employeeOptional = employeeServive.getEmployee();
if(employeeOptional.isPresent()){
    Employee employee= employeeOptional.get();
    System.out.println(employee.getId());
}

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.


12
Opcional se torna uma complicação inútil no momento em que você usa isPresent(). Usamos opcional para não precisarmos testar.
Candied_orange

2
Como outros já disseram, não use isPresent(). Essa é a abordagem incorreta dos opcionais. Use mapou em flatMapvez disso.
Andrés F.

1
@CandiedOrange E, no entanto, a resposta mais votada (dizendo para usar ifPresent) é apenas outra maneira de testar.
immibis

1
@CandiedOrange ifPresent(x)é tanto um teste quanto if(present) {x}. O valor de retorno é irrelevante.
immibis

1
@CandiedOrange No entanto, você disse originalmente "para que não tenhamos que testar". Você não pode dizer "para que não tenhamos que testar" quando você ainda está testando ... o que você é.
Aaron

0

Eu acho que o maior benefício é que, se a assinatura do método retornar um, Employeevocê 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.


1
A maneira mais simples de conseguir isso é usando @Nullablee em @ReturnValuesAreNonnullByDefaultconjunto com a análise estática.
Maaartinus 19/0118

@maaartinus Adicionar ferramentas adicionais na forma de análise estática e documentação nas anotações do decorador é mais simples do que o suporte à API em tempo de compilação?
Sled

A adição de ferramentas adicionais é realmente mais simples, pois não requer alterações de código. Além disso, você quer ter assim mesmo, pois também captura outros bugs. +++ Eu escrevi um script trivial adicionando package-info.javacom @ReturnValuesAreNonnullByDefaulta todos os meus pacotes, então tudo o que tem a fazer é adicionar @Nullableonde você tem que mudar para Optional. 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.
Maaartinus 23/01

Minha maior preocupação 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 que List<@Nullable String>ainda é uma lista de strings, enquanto List<Optional<String>>não é.
Maaartinus

0

Minha resposta é: simplesmente não. Pelo menos pense duas vezes sobre se é realmente uma melhoria.

Uma expressão (retirada de outra resposta) como

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

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

Employee employee = employeeService.getEmployee();
if (employee != null) {
    ID id = employee.getId();
    if (id != null) {
        System.out.println(id);
    }
}

o que torna óbvio que pode haver elsecláusulas.

O estilo funcional

  • parece completamente diferente (e é ilegível para pessoas que não estão acostumadas)
  • é uma dor para depurar
  • não pode ser facilmente estendido para lidar com o caso "ausente" ( orElsenem sempre é suficiente)
  • induz a ignorar o caso "ausente"

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

employeeService
.getEmployee()
?.getId()
?.apply(id => System.out.println(id));

bem conhecido de outras línguas. Concordamos que esta declaração

  • não é (realmente) funcional?
  • é muito semelhante ao código de estilo antigo, apenas muito mais simples?
  • é muito mais legível que o estilo funcional?
  • é amigável ao depurador?

Você parece estar descrevendo a implementação desse conceito pelo C # com um recurso de linguagem totalmente diferente da implementação do Java com uma classe de biblioteca principal. Eles têm a mesma aparência para mim
Caleth

@ Caleth De jeito nenhum. Poderíamos falar sobre o "conceito de simplificar uma avaliação com segurança nula", mas eu não chamaria isso de "conceito". Podemos dizer que Java implementa a mesma coisa usando uma classe de biblioteca principal, mas é apenas uma bagunça. Basta começar 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 o Optional. 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.
Maaartinus 5/0318

Biblioteca de classes: 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" é Optionalaka NullableakaMaybe
Caleth

Não entendo por que você acha que um deles é "abuso feio" e o outro é bom. Eu pude entender você pensando tanto em "abuso feio" ou em ambos os bons.
Caleth

@ Caleth Uma diferença é que, em vez de adicionar dois pontos de interrogação, é necessário reescrever tudo. O problema não é que o recurso Idioma e os códigos de classe da Biblioteca sejam muito diferentes. Isso está ok. O problema é que o código original e os códigos de classe da Biblioteca diferem bastante. Mesma ideia, código diferente. Isso é ruim. +++O que é abusado são lamdas para lidar com a nulidade. Lamdas são boas, mas Optionalnão são. A nulidade simplesmente precisa de um suporte de idioma adequado, como no Kotlin. Outro problema: List<Optional<String>>não está relacionado List<String>, onde precisaríamos que eles estivessem relacionados.
Maaartinus

0

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.