web-dev-qa-db-fra.com

Chaînage des options dans Java 8

Vous cherchez un moyen d'enchaîner les optionnels afin que le premier qui est présent soit renvoyé. Si aucun n'est présent, Optional.empty() devrait être renvoyé.

En supposant que j'ai plusieurs méthodes comme celle-ci:

Optional<String> find1()

J'essaye de les enchaîner:

Optional<String> result = find1().orElse( this::find2 ).orElse( this::find3 );

mais bien sûr, cela ne fonctionne pas car orElse attend une valeur et orElseGet attend un Supplier.

55
piler

Utilisez un flux:

Stream.of(find1(), find2(), find3())
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

Si vous devez évaluer les méthodes de recherche paresseusement, utilisez les fonctions de fournisseur:

Stream.of(this::find1, this::find2, this::find3)
    .map(Supplier::get)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();
80
Sauli Tähkäpää

Vous pouvez le faire comme ceci:

Optional<String> resultOpt = Optional.of(find1()
                                .orElseGet(() -> find2()
                                .orElseGet(() -> find3()
                                .orElseThrow(() -> new WhatEverException()))));

Bien que je ne suis pas sûr que cela améliore la lisibilité IMO. La goyave permet de chaîner les options:

import com.google.common.base.Optional;

Optional<String> resultOpt = s.find1().or(s.find2()).or(s.find3());

Cela pourrait être une autre alternative à votre problème, mais n'utilise pas la classe optionnelle standard dans le JDK.

Si vous souhaitez conserver l'API standard, vous pouvez écrire une méthode d'utilitaire simple:

static <T> Optional<T> or(Optional<T> first, Optional<T> second) {
    return first.isPresent() ? first : second;
}

et alors:

Optional<String> resultOpt = or(s.find1(), or(s.find2(), s.find3()));

Si vous avez beaucoup d’options en chaîne, il est peut-être préférable d’utiliser l’approche Stream comme indiqué précédemment.

30
Alexis C.

Inspiré par la réponse de Sauli, il est possible d'utiliser la méthode flatMap().

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
  .findFirst();

Convertir une option en un flux est fastidieux. Apparemment, cela va être corrigé avec JDK9 . Donc, cela pourrait être écrit comme

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(Optional::stream)
  .findFirst();

Mise à jour après Java 9 a été publié

Bien que la question initiale concernait Java 8, Optional::or a été introduit dans Java 9. Le problème pourrait être résolu comme suit

Optional<String> result = find1()
  .or(this::find2)
  .or(this::find3);
23
Indrek Ots