it-swarm-eu.dev

Funktionsweise von Java 8 Optional.ifPresent und If-Not-Present?

In Java 8 möchte ich etwas mit einem Optional-Objekt tun, wenn es vorhanden ist, und etwas anderes tun, wenn es nicht vorhanden ist.

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

Dies ist jedoch kein "funktionaler Stil".

Optional hat eine ifPresent()-Methode, aber ich kann keine orElse()-Methode verketten.

Daher kann ich nicht schreiben:

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

Als Antwort auf @assylias glaube ich nicht, dass Optional.map() für den folgenden Fall funktioniert:

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;
});

In diesem Fall aktualisiere ich seine Eigenschaft und speichere sie in der Datenbank, wenn opt vorhanden ist. Wenn es nicht verfügbar ist, erstelle ich ein neues obj und speichere es in der Datenbank.

Beachten Sie, dass ich in den beiden Lambdas null zurückgeben muss.

Wenn jedoch opt vorhanden ist, werden beide Lambdas ausgeführt. obj wird aktualisiert und ein neues Objekt wird in der Datenbank gespeichert. Dies liegt an dem return null im ersten Lambda. Und orElseGet() wird weiter ausgeführt.

195
smallufo

Für mich ist die Antwort von @Dane White in Ordnung, zuerst habe ich Runnable nicht gemocht, aber ich konnte keine Alternativen finden, hier eine andere Implementierung, die mir lieber war

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;
    }
}

Dann :

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

Update 1:

die obige Lösung für die traditionelle Art der Entwicklung, wenn Sie den Wert haben und verarbeiten möchten, aber was ist, wenn ich die Funktionalität und die Ausführung definieren möchte, dann überprüfen Sie unten die Verbesserung.

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();
}
}

Dann könnte verwendet werden als:

    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);

In diesem neuen Code haben Sie 3 Dinge:

  1. kann Funktionalität vor dem Bestehen des Objekts einfach definieren.
  2. erstellen Sie nicht für jedes optionale Objekt eine Option, nur eine, Sie haben also weniger Speicher als weniger GC.
  3. es implementiert Consumer zur besseren Verwendung mit anderen Komponenten.

übrigens ist der Name jetzt beschreibender, es ist eigentlich Consumer>

77

Wenn Sie Java 9 verwenden, können Sie die ifPresentOrElse() -Methode verwenden:

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

Siehe ausgezeichnet Optional in Java 8 Spickzettel .

Es bietet alle Antworten für die meisten Anwendungsfälle.

Kurze Zusammenfassung unten

ifPresent () - etwas tun, wenn "Optional" eingestellt ist

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

filter () - bestimmte optionale Werte ablehnen (herausfiltern).

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

map () - transformiere den Wert, falls vorhanden

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

orElse ()/orElseGet () - wird leer Optional auf Standard T

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

orElseThrow () - wirft träge Ausnahmen auf leer. Optional

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

Eine Alternative ist:

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

Ich glaube nicht, dass es die Lesbarkeit verbessert.

Oder verwenden Sie, wie Marko vorgeschlagen hat, einen ternären Operator:

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

Eine andere Lösung wäre die Verwendung von Funktionen höherer Ordnung wie folgt

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

Es gibt keine großartige Möglichkeit, dies aus der Box heraus zu tun. Wenn Sie Ihre Bereinigungssyntax regelmäßig verwenden möchten, können Sie eine Dienstprogrammklasse erstellen, die Ihnen hilft:

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);
    }
}

Dann können Sie einen statischen Import an anderer Stelle verwenden, um eine Syntax zu erhalten, die dem entspricht, wonach Sie suchen:

import static com.example.OptionalEx.ifPresent;

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

Wenn Sie nur Java 8 oder niedriger verwenden können:

1) Wenn Sie noch keinen spring-data haben, ist der beste Weg:

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

Jetzt weiß ich, dass es für jemanden nicht so lesbar und sogar schwer zu verstehen ist, aber für mich persönlich sieht es gut aus, und ich sehe für diesen Fall keinen anderen fließenden Weg.

2) Wenn Sie Glück haben und spring-data verwenden können, ist der beste Weg Optionals # ifPresentOrElse :

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

Wenn Sie Java 9 verwenden können, sollten Sie auf jeden Fall gehen mit:

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

Eine andere Lösung könnte folgende sein:

So verwenden Sie es:

    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"));

Oder falls Sie im gegenteiligen Anwendungsfall zutrifft:

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

Dies sind die Zutaten:

  1. Etwas modifizierte Funktionsschnittstelle, nur für die "elseApply" -Methode
  2. Optionale Erweiterung
  3. Ein bisschen Gießen :-)

Die "kosmetisch" erweiterte Funktionsschnittstelle.

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

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

}

Und die optionale Wrapper-Klasse für die Erweiterung:

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);
    }
}

Dies sollte den Trick erfüllen und könnte als grundlegende Vorlage für den Umgang mit solchen Anforderungen dienen.

Die Grundidee hier ist folgende. In einer nicht funktionalen Programmierwelt würden Sie wahrscheinlich eine Methode implementieren, die zwei Parameter verwendet, wobei der erste eine Art ausführbaren Code ist, der ausgeführt werden sollte, falls der Wert verfügbar ist, und der andere Parameter der ausführbare Code ist, der im Falle der Wert ist nicht verfügbar. Zur besseren Lesbarkeit können Sie mit curring die Funktion von zwei Parametern in zwei Funktionen von jeweils einem Parameter aufteilen. Das habe ich hier grundsätzlich getan.

Hinweis: Opt stellt auch den anderen Anwendungsfall bereit, in dem Sie einen Code ausführen möchten, falls der Wert nicht verfügbar ist. Dies könnte auch über Optional.filter.stuff erfolgen, aber ich fand das viel lesbarer.

Hoffentlich hilft das!

Gute Programmierung :-)

2

Das beschriebene Verhalten kann durch Verwendung von Vavr (früher als Javaslang bekannt) erreicht werden, einer objektfunktionalen Bibliothek für Java 8+, die die meisten Scala-Konstrukte implementiert (wobei Scala eine ausdrucksstärkere Sprache mit einem viel umfangreicheren Typensystem ist) auf JVM). Es ist eine sehr gute Bibliothek, die Sie zu Ihren Java-Projekten hinzufügen können, um reinen Funktionscode zu schreiben.

Vavr stellt die Option-Monade bereit, die Funktionen für den Optionstyp bereitstellt, z.

  • fold: um den Wert der Option in beiden Fällen abzubilden (definiert/leer)
  • onEmpty: Ermöglicht die Ausführung einer Runnable, wenn die Option leer ist
  • peek: Ermöglicht es, den Wert der Option zu konsumieren (sofern definiert).

Option folgt den Monadengesetzen im Unterschied zur optionalen "Pseudomonade" von Java und bietet eine reichhaltigere API. Und natürlich können Sie es von einem Java Optional (und umgekehrt) machen: Option.ofOptional(javaOptional) –Vavr konzentriert sich auf Interoperabilität.

Zum Beispiel:

// 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)
);

Weitere Lektüre

Null Referenz, der Milliarden-Dollar-Fehler

N.B. Dies ist nur ein sehr kleines Beispiel für das, was Vavr anbietet (Pattern Matching, Streams und Lazy-Evaluated-Listen, Monadic-Typen, unveränderliche Sammlungen, ...).

1
Gerard Bosch

Wenn Sie den Wert speichern möchten:

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

Angenommen, Sie haben eine Liste und vermeiden das Problem mit isPresent () (im Zusammenhang mit optionalen Elementen). Sie könnten .iterator (). HasNext () verwenden, um zu überprüfen, ob keine vorhanden sind.

0
Leandro Maro