web-dev-qa-db-fra.com

Facultatif isPresent vs orElse (null)

J'étais en train de mettre à jour les dépendances vers Spring 5 dans mon projet et j'ai été bombardé d'erreurs de compilation où la définition de la méthode de findOne() a été remplacée par findById() qui renvoie maintenant une Optional (corrigez-moi si je me trompe).

Lors de la refactorisation, je suis tombé sur plusieurs approches que je peux choisir d’adopter, et j’aimerais donc avoir des idées sur lesquelles il faut préférer.

1ère approche: 

ExpectedPackage ep = expectedPackageRepository.findById(1).orElse(null);
if(ep != null){
    ep.setDateModified(new Date());
    expectedPackageRepository.saveAndFlush(ep);
}

2ème approche: 

Optional<ExpectedPackage> ep = expectedPackageRepository.findById(1);
if(ep.isPresent()){
    ep.get().setDateModified(new Date());
    expectedPackageRepository.saveAndFlush(ep.get());
}

Ou y a-t-il une troisième et meilleure approche que j'ai manquée? J'ai parcouru plusieurs questions et quelques articles, mais je n'ai pas trouvé de réponse claire.

14
Sadiq Ali

Vous pouvez aussi faire:

expectedPackageRepository.findById(1).ifPresent(
    ep -> {
        ep.setDateModified(new Date());
        expectedPackageRepository.saveAndFlush(ep);
    }
);

Idéalement, vous devriez également extraire la partie entre crochets ({}) dans une méthode distincte. Ensuite, vous pourriez écrire comme ceci:

    expectedPackageRepository.findById(1).ifPresent(this::doSomethingWithEp);

Où:

void doSomethingWithEp(ExpectedPackage ep) {
    ep.setDateModified(new Date());
    expectedPackageRepository.saveAndFlush(ep);
}

Vous pouvez lire la documentation de ifPresent ici: https://docs.Oracle.com/javase/8/docs/api/Java/util/Optional.html#ifPresent-Java.util.function.Consumer-

Comme il est indiqué, il effectuera l'action spécifiée si la valeur est présente et ne fait rien sinon.

19
Poger

L’autre réponse est fondamentalement une refactorisation de votre deuxième approche, qui n’a rien de mal en soi, c’est juste une question de style. Bien sûr, le chaînage et l'extraction sur une méthode séparée rendront cela beaucoup plus lisible et plus clair, sans doute (+1 de ma part), surtout depuis l'utilisation correcte de ifPresent.

J'ajouterais simplement que get, eh bien, a été perçu comme une erreur de conception (ou peut-être un mauvais nom de méthode, probablement dû à l'état d'esprit guava). Utiliser get même s'il est documenté de lancer une exception quand cette valeur est manquante est quelque peu étrange (si vous pensez obtenir des getters ici, vous ne vous attendriez pas à ce que getter lève une exception). Et vous ne vous attendriez pas à ce que get doive être appelé après isPresent, du moins pas lors des toutes premières interactions avec Optional. Ainsi, il a été proposé que get soit obsolète (et, espérons-le, supprimé), ainsi Java-10 ajoute un meilleur ajout orElseThrow() - cela a du sens juste après l'avoir lu, car la partie lancée est dans le nom de la méthode, donc pas de surprises. 

De plus, quelqu'un devrait vous parler de l'utilisation de new Date() qui, lorsqu'il est utilisé avec Optional de Java-8, a l'air bizarre, il existe déjà de bien meilleures classes liées à l'heure et à la date. 

Je ne suis pas non plus très sûr de savoir pourquoi vous mettez à jour une date modifiée manuellement, alors qu'il existe des annotations printanières comme PreUpdate/PrePersist

6
Eugene

Oui, il y a d'autres approches. 

Si vous vous attendez absolument à ce qu'il y ait toujours une valeur, utilisez Optional::orElseThrow pour générer une exception si une valeur null apparaît. 

Si vous vous attendez à ce qu'un null arrive, et qu'une autre instance soit disponible comme option de secours, utilisez Optional::orElse .

Si l'instance de secours n'est pas disponible, mais que vous avez une fonction à appeler pour fournir une instance de secours, utilisez Optional::orElseGet .

Si vous ne vous souciez pas de recevoir un null et que vous ne voulez rien faire quand un null arrive, utilisez Optional::ifPresent . Passez le bloc de code à exécuter si une valeur arrive.

Si vous ne vous souciez que de l'arrivée d'une valeur répondant à certaines exigences, utilisez Optional::filter . Passez un Predicate définissant votre besoin. Par exemple, nous ne nous soucions que si un Optional< String > contient du texte et que ce texte contient Word purple: myOptional.filter( s -> s.contains( "purple" ) ).ifPresent( this::print ) ;. Si null est reçu, notre opération souhaitée (un appel à print dans cet exemple) ne se produit jamais. Si une valeur a été reçue mais n'a pas rencontré notre prédicat, l'opération souhaitée ne se produira jamais.


Faire if( myOptional.isPresent() ) { SomeClass x = myOptional.get() ; … } est valide et sans danger. Mais ce n’est pas l’intention originale de Optional, car c’est fondamentalement la même chose que de faire un if ( null == x ) { … } démodé à l’origine. Les autres méthodes de Optional fournissent un moyen plus clair et élégant d’exprimer vos intentions en vue d’une arrivée nulle possible. 

4
Basil Bourque

vous pouvez aussi faire: 

Optional<ExpectedPackage> updatedPackage = expectedPackageRepository.findById(1).map(ep -> {
    ep.setDateModified(new Date());
    return expectedPackageRepository.saveAndFlush(ep);
});
1
Ritesh