Uso adequado de Optional.ifPresent ()

94

Estou tentando entender o ifPresent()método da OptionalAPI em Java 8.

Eu tenho uma lógica simples:

Optional<User> user=...
user.ifPresent(doSomethingWithUser(user.get()));

Mas isso resulta em um erro de compilação:

ifPresent(java.util.functionError:(186, 74) java: 'void' type not allowed here)

Claro que posso fazer algo assim:

if(user.isPresent())
{
  doSomethingWithUser(user.get());
}

Mas isso é exatamente como um nullcheque desordenado .

Se eu mudar o código para este:

 user.ifPresent(new Consumer<User>() {
            @Override public void accept(User user) {
                doSomethingWithUser(user.get());
            }
        });

O código está ficando mais sujo, o que me faz pensar em voltar ao nullcheque antigo .

Alguma ideia?

Rayman
fonte

Respostas:

154

Optional<User>.ifPresent()leva Consumer<? super User>como argumento. Você está passando uma expressão cujo tipo é vazio. Então isso não compila.

Um Consumidor deve ser implementado como uma expressão lambda:

Optional<User> user = ...
user.ifPresent(theUser -> doSomethingWithUser(theUser));

Ou ainda mais simples, usando uma referência de método:

Optional<User> user = ...
user.ifPresent(this::doSomethingWithUser);

Isso é basicamente a mesma coisa que

Optional<User> user = ...
user.ifPresent(new Consumer<User>() {
    @Override
    public void accept(User theUser) {
        doSomethingWithUser(theUser);
    }
});

A ideia é que a doSomethingWithUser()chamada do método só seja executada se o usuário estiver presente. Seu código executa a chamada de método diretamente e tenta passar seu resultado vazio para ifPresent().

JB Nizet
fonte
2
Esse código está ficando desordenado ... uma verificação de nulo será muito mais limpa. você não acha especialmente que doSomethingWithUser não é um método estático
rayman
4
Qual código? O que você deve usar é o segundo, que chama o método de instância (isto é, não estático) doSomethingWithUser (). Não vejo como está bagunçado. O último código está aí para explicar o equivalente do lambda em um mundo pré-lambda. Não use isso.
JB Nizet de
2
Sim, mas você pode estar acostumado com classes anônimas e, portanto, entender o que o lambda faz ao ver um equivalente de classe anônima. Essa é a questão.
JB Nizet de
1
Você não tem nada para modificar. Deixe como está e use o segundo exemplo:user.ifPresent(this::doSomethingWithUser);
JB Nizet
10
@rayman Se você tem uma função que retorna Optional<User>, geralmente não há necessidade de armazená-la em uma variável local. Apenas encadeie as chamadas de método:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Stuart Marks de
19

Além da resposta de @JBNizet, meu caso de uso geral para ifPresenté combinar .isPresent()e .get():

À moda antiga:

Optional opt = getIntOptional();
if(opt.isPresent()) {
    Integer value = opt.get();
    // do something with value
}

Nova forma:

Optional opt = getIntOptional();
opt.ifPresent(value -> {
    // do something with value
})

Isso, para mim, é mais intuitivo.

cst1992
fonte
7

Use flatMap. Se um valor estiver presente, flatMap retorna um Stream sequencial contendo apenas aquele valor; caso contrário, retorna um Stream vazio. Portanto, não há necessidade de usar ifPresent(). Exemplo:

list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());
Taras Melnyk
fonte
3
Opcional :: stream needs java9
avmohan
7

Por que escrever um código complicado quando você pode torná-lo simples?

Na verdade, se você for absolutamente usar a Optionalclasse, o código mais simples é o que você já escreveu ...

if (user.isPresent())
{
    doSomethingWithUser(user.get());
}

Este código tem a vantagem de ser

  1. legível
  2. fácil de depurar (ponto de interrupção)
  3. não é complicado

Só porque a Oracle adicionou a Optionalclasse em Java 8 não significa que essa classe deva ser usada em todas as situações.

Schlebe
fonte
1
O principal benefício de usar ifPresent é que ele elimina a necessidade de sempre chamar get () manualmente. Chamar get () manualmente está sujeito a erros, pois é fácil esquecer de verificar isPresent primeiro, mas é impossível esquecer se usar ifPresent
dustinroepsch
1
Ok e cada vez que você for usar o objeto 'usuário' você deve chamar .ifPresent (). O código se tornará ilegível rapidamente porque você lerá .ifPresent () muito tempo!
schlebe
2
Para corrigir os erros ortográficos na sua página de perfil ( VB.Net , Netbeans , SqlServer , PostGresql , MySql e Linq, você pode usar meu serviço . Há também uma lista de palavras correspondente .
Peter Mortensen
7

Você pode usar a referência de método como este:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

O método ifPresent()get Consumerobject como um parâmetro e (de JavaDoc ): "Se um valor estiver presente, chame o consumidor especificado com o valor." Valor é a sua variável user.

Ou se este método doSomethingWithUserestiver na Userclasse e não estiver static, você pode usar a referência de método desta forma:

user.ifPresent(this::doSomethingWithUser);
Aleksandr Podkutin
fonte
1
Mas doSomethingWithUser não é um método estático nem é uma classe.
rayman
@rayman Ok, se não for estático, você pode fazer assim:user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Aleksandr Podkutin
7
@AleksandrPodkutin você não deve criar uma nova instância da classe apenas para executar um método, a partir do OP parece que o método está na mesma classe da qual está sendo chamado, portanto, ele deve usaruser.ifPresent(this::doSomethingWithUser);
março
@Marv Não vejo nenhum formulário de afirmação OP de que está na mesma classe. Mas se você tem esses sentimentos, concordo que ele tem que usar user.ifPresent(this::doSomethingWithUser);. Vou adicioná-lo à minha resposta.
Aleksandr Podkutin