Options de chaînage en Java 8

cherche un moyen de enchaîner les options pour que la première qui est présente soit retournée. Si aucun n'est présent Optional.empty() doit être retourné.

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

Optional<String> find1()

j'essaie de les enchaîner:

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

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

40
demandé sur Chirlo 2015-02-14 13:32:47

5 réponses

l'Utilisation d'un Ruisseau:

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

si vous avez besoin d'évaluer les méthodes find paresseusement, utiliser les fonctions de fournisseur:

Stream.of(this::find1, this::find2, this::find3)
    .map(Supplier::get)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();
60
répondu Sauli Tähkäpää 2015-02-16 12:53:08

vous pourriez 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 qu'il améliore la lisibilité IMO. La goyave offre un moyen D'enchaîner les optionnels:

import com.google.common.base.Optional;

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

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

si vous voulez conserver l'API standard, vous pouvez écrire une méthode d'utilité simple:

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

et ensuite:

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

si vous avez beaucoup d'options à chains, peut-être qu'il est préférable d'utiliser L'approche Stream comme les autres mentionnés déjà.

29
répondu Alexis C. 2015-02-16 13:09:22

inspiré de 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 un optionnel en un flux est encombrant. Apparemment, cela va être fixé avec JDK9 . Donc ça pourrait s'écrire comme

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

mise à jour après la sortie de Java 9

bien que la question originale était sur Java 8, Optional::or a été introduit en Java 9. Avec elle, le problème pourrait être résolu comme suit:

Optional<String> result = find1()
  .or(this::find2)
  .or(this::find3);
16
répondu Indrek Ots 2017-10-14 09:58:41

pour effectuer chaînage optionnel première convertir flux en option en utilisant l'une des deux méthodes

  1. findAny() ou la méthode findFirst()
  2. min () et max()

une fois optionnel est obtenu optionnel a deux méthodes d'instance supplémentaires qui sont également présentes dans la classe de flux I. e filter and map(). l'utilisation de ces méthodes et de vérifier sortie d'utiliser ifPresent(Système d'.sortie:: Println)

ex:

Flux s = Stream.de (1,2,3,4);

S. findFirst ().filtre (a)->a+1).ifPresent(System.sortie:: Println)

sortie: 2

0
répondu Adit chandel 2018-05-21 17:48:20

peut-être un de

    public <T> Optional<? extends T> firstOf(Optional<? extends T> first, @SuppressWarnings("unchecked") Supplier<Optional<? extends T>>... supp) {
        if (first.isPresent()) return first;
        for (Supplier<Optional <? extends T>> sup : supp) {
            Optional<? extends T> opt = sup.get();
            if (opt.isPresent()) {
                return opt;
            }
        }
        return Optional.empty();
    }

    public <T> Optional<? extends T> firstOf(Optional<? extends T> first, Stream<Supplier<Optional<? extends T>>> supp) {
        if (first.isPresent()) return first;
        Stream<Optional<? extends T>> present = supp.map(Supplier::get).filter(Optional::isPresent);
        return present.findFirst().orElseGet(Optional::empty);
    }

fera l'affaire.

la première itère sur un réseau de fournisseurs. Le premier Optional<> non vide est retourné. Si nous n'en trouvons pas, nous retournons un Optional vide .

le second fait la même chose avec un Stream de Suppliers qui est traversé, chacun demandé (paresseusement) pour leur valeur, qui est ensuite filtré pour vide Optional s. La première non vide est retourné, ou si aucune n'existe, un vide.

-3
répondu glglgl 2015-02-14 11:00:03