Utilisation correcte de Optional.ifPresent ()

95

J'essaie de comprendre la ifPresent()méthode de l' OptionalAPI dans Java 8.

J'ai une logique simple:

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

Mais cela entraîne une erreur de compilation:

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

Bien sûr, je peux faire quelque chose comme ça:

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

Mais c'est exactement comme un nullchèque encombré .

Si je change le code en ceci:

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

Le code est de plus en plus sale, ce qui me fait penser à revenir à l'ancien nullchèque.

Des idées?

Rayman
la source

Réponses:

155

Optional<User>.ifPresent()prend un Consumer<? super User>comme argument. Vous lui passez une expression dont le type est nul. Donc cela ne compile pas.

Un consommateur est destiné à être implémenté en tant qu'expression lambda:

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

Ou encore plus simple, en utilisant une référence de méthode:

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

C'est fondamentalement la même chose que

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

L'idée est que l' doSomethingWithUser()appel de méthode ne sera exécuté que si l'utilisateur est présent. Votre code exécute directement l'appel de méthode et tente de transmettre son résultat void à ifPresent().

JB Nizet
la source
2
Ce code est de plus en plus encombré ... une vérification nulle sera beaucoup plus propre. ne pensez-vous pas spécialement que doSomethingWithUser n'est pas une méthode statique
rayman
4
Quel code? Celui que vous devez utiliser est le second, qui appelle la méthode d'instance (c'est-à-dire non statique) doSomethingWithUser (). Je ne vois pas comment c'est encombré. Le dernier code est là pour vous expliquer l'équivalent du lambda dans un monde pré-lambda. Ne l'utilisez pas.
JB Nizet
2
Oui, mais vous pourriez être habitué aux classes anonymes et comprendre ainsi ce que fait le lambda en voyant un équivalent de classe anonyme. C'est le but.
JB Nizet
1
Vous n'avez rien à modifier. Laissez-le tel quel et utilisez le deuxième exemple:user.ifPresent(this::doSomethingWithUser);
JB Nizet
10
@rayman Si vous avez une fonction qui renvoie, Optional<User>il n'est souvent pas nécessaire de la stocker dans une variable locale. Il suffit d'enchaîner les appels de méthode:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Stuart marque le
20

En plus de la réponse de @ JBNizet, mon cas d'utilisation général ifPresentest de combiner .isPresent()et .get():

Ancienne façon:

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

Nouvelle façon:

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

Ceci, pour moi, est plus intuitif.

cst1992
la source
9

Pourquoi écrire du code compliqué alors que vous pouviez le simplifier?

En effet, si vous allez absolument utiliser la Optionalclasse, le code le plus simple est celui que vous avez déjà écrit ...

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

Ce code présente les avantages d'être

  1. lisible
  2. facile à déboguer (point d'arrêt)
  3. pas compliqué

Ce Optionaln'est pas parce qu'Oracle a ajouté la classe dans Java 8 que cette classe doit être utilisée dans toutes les situations.

schlebe
la source
1
Le principal avantage de l'utilisation d'ifPresent est qu'il vous évite de devoir appeler get () manuellement. L'appel manuel de get () est sujet aux erreurs, car il est facile d'oublier de vérifier d'abord isPresent, mais il est impossible pour vous d'oublier si vous utilisez ifPresent
dustinroepsch
1
Ok et à chaque fois que vous utiliserez l'objet 'utilisateur', vous devez appeler .ifPresent (). Le code deviendra rapidement illisible car vous lirez trop longtemps .ifPresent ()!
schlebe
2
Pour corriger les fautes d'orthographe sur votre page de profil ( VB.Net , Netbeans , SqlServer , PostGresql , MySql et Linq, vous pouvez utiliser mon service . Il existe également une liste de mots correspondante .
Peter Mortensen
7

Utilisez flatMap. Si une valeur est présente, flatMap renvoie un Stream séquentiel contenant uniquement cette valeur, sinon renvoie un Stream vide. Il n'y a donc pas besoin d'utiliser ifPresent(). Exemple:

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

Vous pouvez utiliser une référence de méthode comme celle-ci:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

Méthode ifPresent()get Consumerobject comme paramètre et (à partir de JavaDoc ): "Si une valeur est présente, invoquez le consommateur spécifié avec la valeur." Value it est votre variable user.

Ou si cette méthode doSomethingWithUserest dans la Userclasse et qu'elle ne l'est pas static, vous pouvez utiliser une référence de méthode comme celle-ci:

user.ifPresent(this::doSomethingWithUser);
Aleksandr Podkutin
la source
1
Mais doSomethingWithUser n'est ni une méthode statique ni une classe.
rayman
@rayman Ok, sinon statique, vous pouvez faire comme ceci:user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Aleksandr Podkutin
7
@AleksandrPodkutin, vous ne devriez pas créer une nouvelle instance de la classe juste pour exécuter une méthode, à partir de l'OP, il semble que la méthode est dans la même classe que celle depuis laquelle elle est appelée, il devrait donc utiliseruser.ifPresent(this::doSomethingWithUser);
Marv
@Marv Je ne vois aucune forme d'affirmation OP indiquant qu'elle est dans la même classe. Mais si vous avez de tels sentiments, je suis d'accord qu'il doit utiliser user.ifPresent(this::doSomethingWithUser);. Je vais l'ajouter à ma réponse.
Aleksandr Podkutin