web-dev-qa-db-fra.com

Style fonctionnel de Java 8: Optional.ifPresent et if-not-Present?

Dans Java 8, je souhaite modifier quelque chose avec un objet Optional s'il est présent et effectuer une autre opération s'il n'est pas présent.

if (opt.isPresent()) {
  System.out.println("found");
} else {
  System.out.println("Not found");
}

Ce n'est pas un «style fonctionnel», cependant.

Optional a une méthode ifPresent(), mais je ne parviens pas à chaîner une méthode orElse().

Ainsi, je ne peux pas écrire:

opt.ifPresent( x -> System.out.println("found " + x))
   .orElse( System.out.println("NOT FOUND"));

En réponse à @assylias, je ne pense pas que Optional.map() fonctionne dans le cas suivant:

opt.map( o -> {
  System.out.println("while opt is present...");
  o.setProperty(xxx);
  dao.update(o);
  return null;
}).orElseGet( () -> {
  System.out.println("create new obj");
  dao.save(new obj);
  return null;
});

Dans ce cas, lorsque opt est présent, je mets à jour sa propriété et l’enregistre dans la base de données. Lorsqu'il n'est pas disponible, je crée une nouvelle variable obj et l'enregistre dans la base de données.

Notez dans les deux lambdas je dois retourner null.

Mais lorsque opt est présent, les deux lambdas seront exécutés. obj sera mis à jour et un nouvel objet sera enregistré dans la base de données. C'est à cause du return null dans le premier lambda. Et orElseGet() continuera à exécuter.

195
smallufo

Pour moi, la réponse de @Dane White est OK. D'abord, je n'aimais pas utiliser Runnable mais je ne pouvais pas trouver d'alternative, voici une autre implémentation que j'ai préférée plus

public class OptionalConsumer<T> {
    private Optional<T> optional;

    private OptionalConsumer(Optional<T> optional) {
        this.optional = optional;
    }

    public static <T> OptionalConsumer<T> of(Optional<T> optional) {
        return new OptionalConsumer<>(optional);
    }

    public OptionalConsumer<T> ifPresent(Consumer<T> c) {
        optional.ifPresent(c);
        return this;
    }

    public OptionalConsumer<T> ifNotPresent(Runnable r) {
        if (!optional.isPresent())
            r.run();
        return this;
    }
}

Ensuite :

Optional<Any> o = Optional.of(...);
OptionalConsumer.of(o).ifPresent(s ->System.out.println("isPresent "+s))
            .ifNotPresent(() -> System.out.println("! isPresent"));

Mise à jour 1:

la solution ci-dessus pour le mode de développement traditionnel lorsque vous avez la valeur et que vous souhaitez la traiter, mais que se passe-t-il si je souhaite définir la fonctionnalité et que l'exécution sera effectuée, vérifiez ci-dessous l'amélioration;

public class OptionalConsumer<T> implements Consumer<Optional<T>> {
private final Consumer<T> c;
private final Runnable r;

public OptionalConsumer(Consumer<T> c, Runnable r) {
    super();
    this.c = c;
    this.r = r;
}

public static <T> OptionalConsumer<T> of(Consumer<T> c, Runnable r) {
    return new OptionalConsumer(c, r);
}

@Override
public void accept(Optional<T> t) {
    if (t.isPresent())
        c.accept(t.get());
    else
        r.run();
}
}

Alors pourrait être utilisé comme:

    Consumer<Optional<Integer>> c=OptionalConsumer.of(System.out::println, ()->{System.out.println("Not fit");});
    IntStream.range(0, 100).boxed().map(i->Optional.of(i).filter(j->j%2==0)).forEach(c);

Dans ce nouveau code, vous avez 3 choses:

  1. peut définir une fonctionnalité avant d’exister d’objet easy.
  2. ne créant pas de référence d'objet pour chaque option, vous avez donc moins de mémoire que moins de GC.
  3. il implémente consommateur pour une meilleure utilisation avec d'autres composants.

en passant, maintenant, son nom est plus descriptif, il est en fait Consommateur>

77

Si vous utilisez Java 9, vous pouvez utiliser ifPresentOrElse() method:

opt.ifPresentOrElse(
   value -> System.out.println("Found: " + value),
   () -> System.out.println("Not found")
);
125
ZhekaKozlov

Voir excellent En option dans Java 8 aide-mémoire .

Il fournit toutes les réponses à la plupart des cas d'utilisation.

Bref résumé ci-dessous

ifPresent () - fait quelque chose quand Facultatif est défini

opt.ifPresent(x -> print(x)); 
opt.ifPresent(this::print);

filter () - rejette (filtre) certaines valeurs facultatives.

opt.filter(x -> x.contains("ab")).ifPresent(this::print);

map () - Transforme la valeur si présente

opt.map(String::trim).filter(t -> t.length() > 1).ifPresent(this::print);

orElse ()/orElseGet () - Vider vide Facultatif avec T par défaut

int len = opt.map(String::length).orElse(-1);
int len = opt.
    map(String::length).
    orElseGet(() -> slowDefault());     //orElseGet(this::slowDefault)

orElseThrow () - Lève paresseusement des exceptions sur empty Facultatif

opt.
filter(s -> !s.isEmpty()).
map(s -> s.charAt(0)).
orElseThrow(IllegalArgumentException::new);
67
Bartosz Bilicki

Une alternative est:

System.out.println(opt.map(o -> "Found")
                      .orElse("Not found"));

Je ne pense pas que cela améliore la lisibilité cependant.

Ou, comme Marko l'a suggéré, utilisez un opérateur ternaire:

System.out.println(opt.isPresent() ? "Found" : "Not found");
45
assylias

Une autre solution serait d'utiliser les fonctions d'ordre supérieur comme suit

opt.<Runnable>map(value -> () -> System.out.println("Found " + value))
   .orElse(() -> System.out.println("Not Found"))
   .run();
32
user5057016

Il n'y a pas de bonne façon de le faire en dehors de la boîte. Si vous souhaitez utiliser régulièrement votre syntaxe de nettoyage, vous pouvez créer une classe d’utilitaires pour vous aider:

public class OptionalEx {
    private boolean isPresent;

    private OptionalEx(boolean isPresent) {
        this.isPresent = isPresent;
    }

    public void orElse(Runnable runner) {
        if (!isPresent) {
            runner.run();
        }
    }

    public static <T> OptionalEx ifPresent(Optional<T> opt, Consumer<? super T> consumer) {
        if (opt.isPresent()) {
            consumer.accept(opt.get());
            return new OptionalEx(true);
        }
        return new OptionalEx(false);
    }
}

Vous pouvez ensuite utiliser une importation statique ailleurs pour obtenir une syntaxe proche de celle que vous recherchez:

import static com.example.OptionalEx.ifPresent;

ifPresent(opt, x -> System.out.println("found " + x))
    .orElse(() -> System.out.println("NOT FOUND"));
17
Dane White

Si vous ne pouvez utiliser que Java 8 ou une version inférieure:

1) Si vous n'avez pas spring-data, le meilleur moyen pour l'instant est:

opt.<Runnable>map(param -> () -> System.out.println(param))
      .orElse(() -> System.out.println("no-param-specified"))
      .run();

Maintenant, je sais que ce n'est pas si lisible et même difficile à comprendre pour quelqu'un, mais ça me va personnellement et je ne vois pas d'autre moyen agréable de parler de cette affaire.

2) si vous êtes assez chanceux et que vous pouvez utiliser spring-data le meilleur moyen est Optionals # ifPresentOrElse :

Optionals.ifPresentOrElse(opt, System.out::println,
      () -> System.out.println("no-param-specified"));

Si vous pouvez utiliser Java 9, vous devez absolument utiliser:

opt.ifPresentOrElse(System.out::println,
      () -> System.out.println("no-param-specified"));
3
Tyulpan Tyulpan

Une autre solution pourrait être la suivante:

Voici comment vous l'utilisez:

    final Opt<String> opt = Opt.of("I'm a cool text");
    opt.ifPresent()
        .apply(s -> System.out.printf("Text is: %s\n", s))
        .elseApply(() -> System.out.println("no text available"));

Ou si vous dans le cas où le cas d'utilisation inverse est vrai:

    final Opt<String> opt = Opt.of("This is the text");
    opt.ifNotPresent()
        .apply(() -> System.out.println("Not present"))
        .elseApply(t -> /*do something here*/);

Ce sont les ingrédients:

  1. Petite interface de fonction modifiée, juste pour la méthode "elseApply"
  2. Amélioration facultative
  3. Un peu de courant :-)

L'interface fonctionnelle améliorée "de manière esthétique".

@FunctionalInterface
public interface Fkt<T, R> extends Function<T, R> {

    default R elseApply(final T t) {
        return this.apply(t);
    }

}

Et la classe wrapper facultative pour l'amélioration:

public class Opt<T> {

    private final Optional<T> optional;

    private Opt(final Optional<T> theOptional) {
        this.optional = theOptional;
    }

    public static <T> Opt<T> of(final T value) {
        return new Opt<>(Optional.of(value));
    }

    public static <T> Opt<T> of(final Optional<T> optional) {
        return new Opt<>(optional);
    }

    public static <T> Opt<T> ofNullable(final T value) {
        return new Opt<>(Optional.ofNullable(value));
    }

    public static <T> Opt<T> empty() {
        return new Opt<>(Optional.empty());
    }

    private final BiFunction<Consumer<T>, Runnable, Void> ifPresent = (present, notPresent) -> {
        if (this.optional.isPresent()) {
            present.accept(this.optional.get());
        } else {
            notPresent.run();
        }
        return null;
    };

   private final BiFunction<Runnable, Consumer<T>, Void> ifNotPresent = (notPresent, present) -> {
        if (!this.optional.isPresent()) {
            notPresent.run();
        } else {
            present.accept(this.optional.get());
        }
        return null;
    };

    public Fkt<Consumer<T>, Fkt<Runnable, Void>> ifPresent() {
        return Opt.curry(this.ifPresent);
    }

    public Fkt<Runnable, Fkt<Consumer<T>, Void>> ifNotPresent() {
        return Opt.curry(this.ifNotPresent);
    }

    private static <X, Y, Z> Fkt<X, Fkt<Y, Z>> curry(final BiFunction<X, Y, Z> function) {
        return (final X x) -> (final Y y) -> function.apply(x, y);
    }
}

Cela devrait faire l'affaire et pourrait servir de modèle de base pour faire face à de telles exigences.

L'idée de base est la suivante. Dans un monde de programmation de style non fonctionnel, vous implémenterez probablement une méthode prenant deux paramètres. Le premier est une sorte de code exécutable qui devrait être exécuté si la valeur est disponible et la valeur n'est pas disponible. Dans un souci de meilleure lisibilité, vous pouvez utiliser la méthode Curring pour fractionner la fonction de deux paramètres en deux fonctions d’un paramètre chacune. C'est ce que j'ai essentiellement fait ici.

Astuce: Opt fournit également l'autre cas d'utilisation dans lequel vous souhaitez exécuter un morceau de code au cas où la valeur ne serait pas disponible. Cela pourrait être fait aussi via Optional.filter.stuff mais j'ai trouvé cela beaucoup plus lisible.

J'espère que cela pourra aider!

Bonne programmation :-)

2
Alessandro Giusa

Le comportement décrit peut être obtenu en utilisant Vavr (anciennement Javaslang), une bibliothèque à objets fonctionnels pour Java 8+, qui implémente la plupart des constructions Scala (Scala étant un langage plus expressif avec un système de type plus riche sur JVM). C'est une très bonne bibliothèque à ajouter à vos projets Java pour écrire du code fonctionnel pur.

Vavr fournit la monade Option qui fournit des fonctions permettant de travailler avec le type Option, telles que:

  • fold: pour mapper la valeur de l'option sur les deux cas (défini/vide)
  • onEmpty: permet d'exécuter une Runnable quand l'option est vide
  • peek: permet de consommer la valeur de l'option (si définie).

Option suit les lois des monades à la différence de la "pseudo-monade" facultative de Java et fournit une API plus riche. Et bien sûr, vous pouvez le créer à partir d'un fichier Java facultatif (et inversement): Option.ofOptional(javaOptional) –Vavr est axé sur l'interopérabilité.

Aller à l'exemple:

// AWESOME Vavr functional collections (immutable for the gread good :)
// fully convertible to Java's counterparts.
final Map<String, String> map = Map("key1", "value1", "key2", "value2");

final Option<String> opt = map.get("nonExistentKey"); // you're safe of null refs!

final String result = opt.fold(
        () -> "Not found!!!",                // Option is None
        val -> "Found the value: " + val     // Option is Some(val)
);

Lectures complémentaires

Référence nulle, l'erreur d'un milliard de dollars

N.B. Ceci n'est qu'un très petit exemple de ce que Vavr propose (correspondance de modèle, flux a.k.a. listes évaluées paresseuses, types monadiques, collections immuables, ...).

1
Gerard Bosch

Si vous voulez stocker la valeur:

Pair.of<List<>, List<>> output = opt.map(details -> Pair.of(details.a, details.b))).orElseGet(() -> Pair.of(Collections.emptyList(), Collections.emptyList()));
0
Jhutan Debnath

En supposant que vous ayez une liste et en évitant le problème isPresent () (lié aux options), vous pouvez utiliser .iterator (). HasNext () pour vérifier si elle n’est pas présente.

0
Leandro Maro