1. ホーム

[解決済み】Java 8のOptional.ifPresentとif-not-Presentの機能的なスタイル?

2022-04-14 14:38:39

質問

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 を作成し、データベースに保存する。

2つのラムダで、私は null .

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

解決方法は?

私にとっては、@Dane Whiteの回答はOKです。 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. 各Optionalのオブジェクト参照を作成せず、1つだけなので、メモリが少なく、GCも少なくなります。
  3. 他のコンポーネントでより良く使用できるようにコンシューマを実装しています。

ちなみに、この名前はよりわかりやすく、Consumer<Optional<?>>.と呼ばれています。