web-dev-qa-db-ja.com

Java 8のOptional.ifPresentおよびif-not-presentの機能スタイル

Java 8では、私はOptionalオブジェクトが存在すればそれに対して何かをし、存在しなければ別のことをしたいのです。

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

しかし、これは「機能的なスタイル」ではありません。

OptionalにはifPresent()メソッドがありますが、orElse()メソッドをチェーニングすることはできません。

したがって、私は書くことができません:

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

@assyliasへの返信として、Optional.map()は次のような場合にはうまくいきません。

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

この場合、optが存在するときは、そのプロパティを更新してデータベースに保存します。利用できない場合は、新しいobjを作成してデータベースに保存します。

二つのラムダの中で私はnullを返さなければならないことに注意してください。

しかしoptが存在するとき、両方のラムダが実行されます。 objが更新され、新しいオブジェクトがデータベースに保存されます。これは最初のラムダのreturn nullのためです。そしてorElseGet()は実行を続けます。

213
smallufo

私にとって@Dane Whiteの答えは大丈夫です、最初はRunnableを使うのが好きではありませんでしたが、他に選択肢が見つかりませんでした。

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

その後:

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

アップデート1:

価値がありそれを処理したい場合の伝統的な開発方法に対する上記の解決策ですが、機能と実行を定義したいのであれば、以下の機能強化をチェックしてください。

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

それからとして使用することができます:

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

この新しいコードでは、3つのことがあります。

  1. オブジェクトの存在する前に機能を簡単に定義できます。
  2. 各オプションに対してオブジェクト参照を作成しない、1つだけ、あなたはとても少ないメモリより少ないGCを持ちます。
  3. それは他のコンポーネントとのより良い使用法のために消費者を実装しています。

ところで、今ではその名前はもっと説明的になっています。実際にはConsumerです。

86

Java 9を使用している場合は、 ifPresentOrElse() methodを使用できます。

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

Excellent Java 8チートシートではオプション を参照してください。

それはほとんどのユースケースにすべての答えを提供します。

以下に簡単な要約

ifPresent() - Optionalが設定されているときに何かをする

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

filter() - 特定のオプションの値を拒否(除外)します。

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

map() - 存在する場合は値を変換する

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

orElse()/ orElseGet() - 空にするデフォルトはT

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

orElseThrow() - 空の場合に遅延的に例外をスローします

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

代替案は以下のとおりです。

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

それは読みやすさを向上させるとは思わない。

あるいはMarkoが示唆しているように、三項演算子を使う:

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

別の解決策は、次のように高階関数を使うことです。

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

箱から出してそれを行うための素晴らしい方法はありません。あなたが定期的にあなたのよりきれいな構文を使用したいならば、あなたは助けるためにユーティリティクラスを作成することができます:

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

それから他の場所で静的インポートを使用して、あなたがしているものに近い構文を得ることができます。

import static com.example.OptionalEx.ifPresent;

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

別の解決策は以下の通りです。

これはあなたがそれをどのように使うかです:

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

あるいは、あなたが反対のユースケースの場合には本当です:

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

これは成分です:

  1. "elseApply"メソッド用に、少し変更されたFunctionインターフェース
  2. オプションの機能強化
  3. 少し曲がっています:-)

「化粧品的に」機能強化された機能インターフェース。

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

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

}

そして強化のためのOptionalラッパークラス:

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

これはうまくいくはずであり、そのような要件に対処する方法の基本的なテンプレートとして役立つことができます。

ここでの基本的な考え方は以下の通りです。非関数型プログラミングの世界では、おそらく2つのパラメータを取るメソッドを実装するでしょう。最初のパラメータは値が利用可能である場合に実行されるべき実行可能コードの一種であり、他のパラメータは値が利用できません。読みやすくするために、curringを使用して、2つのパラメータの関数をそれぞれ1つのパラメータの2つの関数に分割することができます。これは私が基本的にここでやったことです。

ヒント:Optは、値が利用できない場合に備えて、コードの一部を実行したいという他のユースケースも提供します。これはOptional.filter.stuffでも実行できますが、これはもっと読みやすくなっています。

それが役立つことを願っています!

良いプログラミング:-)

3

Java 8以下しか使用できない場合

1)spring-dataを持っていないのであれば、これまでのところ最良の方法です。

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

2)幸運でspring-dataを使うことができるなら、 Optionals#ifPresentOrElse

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

あなたがJava 9を使うことができるなら、あなたは間違いなく行くべきです:

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

記述された振る舞いは Vavr (以前はJavaslangとして知られていました)、ほとんどのScalaコンストラクトを実装するJava 8+のためのオブジェクト関数ライブラリを使うことによって達成することができます。純粋な機能コードを書くためにあなたのJavaプロジェクトに追加するのは非常に良いライブラリです。

Vavrは、以下のようなOption型で機能する関数を提供するOptionモナドを提供します。

  • fold:両方の場合でオプションの値をマッピングします(defined/empty)
  • onEmpty:オプションが空のときにRunnableを実行することを許可します
  • peek:オプションの値を消費することを許可します。

Optionは、JavaのOptionalの「擬似モナド」とは異なるモナドの法則に従い、より豊富なAPIを提供します。

1
Gerard Bosch

値を保存したい場合は:

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

リストがあり、isPresent()の問題(オプション関連)を回避すると仮定すると、.iterator()。hasNext()を使用して、存在しないかどうかを確認できます。

0
Leandro Maro