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


274

Java 8では、Optionalオブジェクトが存在する場合は何かを行い、存在しない場合は別のことを行います。

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

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

OptionalifPresent()メソッドを持っていますが、メソッドをチェーンできません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()、実行し続けます。


53
最初のサンプルを使用します。それは美しいです
Sotirios Delimanolis 2014年

3
その動作用に設計されていないAPIを使用するときは、特定の動作を強制することをやめることをお勧めします。あなたの最初の例は、いくつかの小さなスタイルのコメントを除いて私にはうまく見えますが、それらは意見があります。
skiwi、2014年

4
@smallufo:置き換えるreturn null;return o;(両方)。しかし、私はあなたが間違った場所で働いているという強い気持ちを持っています。あなたはそれを生み出したサイトで働くべきですOptional。その場所には、中間体なしで目的の操作を実行する方法があるはずOptionalです。
Holger

10
Java 9は問題の解決策を実装します:iteratrlearning.com/java9/2016/09/05/java9-optional.html
pisaruk

2
これがなかなかできないのは意図的なものだと思います。Optionalはフロー制御ではなく、値の変換を行う必要があります。私はifPresentこれが矛盾していることを知っています。他のすべてのメソッドは、アクションではなく値を参照します。
AlikElzin-kilaka 2017

回答:


109

私にとって@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. オプションごとにオブジェクト参照を作成しないのは1つだけです。メモリが少なく、GCも少なくなります。
  3. 他のコンポーネントとの使用を改善するためにコンシューマーを実装しています。

ちなみに、その名前はよりわかりやすくなりました実際には消費者です>


3
Optional.of(o)の代わりにOptional.ofNullable(o)を使用する必要があります
traeper

2
使用する値がnullであるかどうかが不明で、NPEに直面する必要がない場合、およびnullでないことが確実であるか、getの場合は気にしない場合は、ofNullableを使用する必要がありますNPE。
Bassem Reda Zohdy 2017年

1
クラスOptionalConsumerは、コード内のif / elseよりも優れていると思います。ありがとう!:)
witek1902 2018

207

Java 9以降を使用している場合は、次のifPresentOrElse()メソッドを使用できます。

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

3
Scalaでのパターンマッチングとほぼ同じくらいきれいなので、いいですね
sscarduzio 2017年

そのような2つのラムダはかなり醜いです。これらのケースではif / elseの方がずっとクリーンだと思います。
john16384

1
@ john16384 OK、見づらい場合は、答えを削除します(いいえ)。
ZhekaKozlov

これは非常に良いことですが、ifPresentOrElseが使用できないため、この質問は特にJDK8向けのものでした。
hreinn

81

Java 9の紹介

ifPresentOrElse値が存在する場合は、その値を使用して指定されたアクションを実行します。それ以外の場合は、指定された空のアクションを実行します。

優れたOptional in 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);

66
これは実際にはOPの質問に答えるものではありません。それは多くの一般的な用途に答えますが、OPが尋ねたものではありません。
キャプテンマン

1
@CaptainManは実際にそれを行います。opt.map( "found")。orElse( "not found")式が請求書を埋めます。
Matt

4
@Mattいいえ、OPはオプションが存在する/しない場合に実行するアクションを具体的に要求します。存在する場合と存在しない場合に値を返しません。OPは、orElseGetを使用した質問で同様のことについて言及し、それが機能しない理由を説明しています。
キャプテンマン

2
@CaptainMan私はあなたの要点を理解しています。彼がからnullを返さなかった場合、彼はmapそれを機能させることができると思いますが、DAOを呼び出せるように機能的なソリューションを要求するのは少し奇妙です。私には、このmap.orElseブロックから更新された/新しいオブジェクトを返し、返されたオブジェクトに対して必要なことを行う方が理にかなっているようです。
Matt

1
mapはストリーム自体に焦点を当てていると思いますが、「ストリーム内のこの要素のステータスに応じて別のオブジェクトに対して物事を行う」ことを意図していません。ifPresentOrElseJava 9で追加されたことを知っておくとよい
WesternGun

53

別の方法は次のとおりです。

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

読みやすさは向上しないと思います。

または、Markoが提案したように、三項演算子を使用します。

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

2
@assyliasに感謝しますが、この場合はOptional.map()が機能するとは思いません(コンテキストの更新を参照してください)。
smallufo 2014年

2
@smallufo new Object();最初のラムダで戻る必要がありますが、正直に言うと非常に醜くなります。更新された例では、if / elseを使用します。
アッシリア

同意して、連鎖のためmapに単に戻るOptionalために使用するmapと、文字通り何かにマップされると想定されている間、コードを理解するのが難しくなります。
ティナ

40

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

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

8
私の目には、これまでのところJDK 9なしの最良の解決策
Semaphor

5
説明は素晴らしいでしょう。なぜランナブルマップ(?)を使用する必要があるのか​​、そしてそのvalue -> () -> syso部分の意味を自問しています。
froehli

この解決策をありがとう!Runnableを使用する理由は、アウトマップが値を返さず、ランナブルを使用するとラムダが返されることと、マップの結果が後で実行されるラムダであることなどです。あなたが値を返すしているのであれば、あなたは次のように使用することができますString result = opt.map(value -> "withOptional").orElse("without optional");
nanotexnik

21

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

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

ありがとう。このソリューションは美しいです。(JDKにそのようなメソッドが組み込まれていない限り)組み込みのソリューションはないかもしれません。OptionalExは非常に役に立ちます。とにかくありがとうございました。
smallufo 2014年

はい、私は結果とそれがサポートするスタイルが好きです。SO、なぜ標準APIではないのですか?
ガスリー2014年

いい答えだ。私たちは同じことをします。API(または言語)に含める必要があることに同意しますが、拒否されました:bugs.openjdk.java.net/browse/JDK-8057557
Garrett Smith

いいね。これは、検討のためにJDK 8.1の一部である必要があります。
peter_pilgrim 2015

35
Optional.ifPresentOrElse()JDK 9に追加されました
Stuart Marks

9

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

2

記載された動作を使用することによって達成することができるVavr器具が最もScalaの構築物(JVM上に構築された方法より豊かな型システムとより表現言語スカラである)こと、(以前Javaslangとして知られている)、ジャワ8+のオブジェクト官能ライブラリ。純粋な機能コードを記述するためにJavaプロジェクトに追加するのに非常に優れたライブラリです。

Vavrは、次のOptionようなオプションタイプを操作する関数を提供するモナドを提供します。

  • fold:両方の場合のオプションの値をマップする(定義済み/空)
  • onEmptyRunnableオプションが空のときにaを実行できます
  • peek:オプションの値を使用できます(定義されている場合)。
  • また、それとはSerializable逆にOptional、メソッドの引数およびインスタンスメンバーとして安全に使用できることを意味します。

オプションは、Javaのオプションの「疑似モナド」とは異なり、モナドの法則に従い、より豊富なAPIを提供します。そしてもちろん、JavaのOptional(およびその逆)から作成することもできます。Option.ofOptional(javaOptional)–Vavrは相互運用性に重点を置いています。

例に行く:

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

参考文献

ヌルリファレンス、10億ドルの間違い

NBこれはVavrが提供するもののほんの少しの例にすぎません(パターンマッチング、遅延別名評価リスト、モナディックタイプ、不変コレクションなど)。


1

別の解決策は次のとおりです:

これはあなたがそれを使う方法です:

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

}

そして、拡張のためのオプションのラッパークラス:

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つのパラメーターを取るメソッドを実装します。最初のパラメーターは、値が使用可能な場合に実行する必要がある実行可能コードの一種で、もう1つのパラメーターは、場合に実行する必要がある実行可能コードです。値は利用できません。読みやすくするために、curringを使用して、2つのパラメーターの関数を、それぞれ1つのパラメーターの2つの関数に分割できます。これは私が基本的にここでやったことです。

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

お役に立てば幸いです。

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


何が機能していないかわかりますか?私はもう一度それをテストしました、そして私にとってそれは働いていますか?
Alessandro Giusa 2017

すみませんが、接続Fktはどこで定義されていますか?最後になりましたが、コンパイルの問題があります:エラー:(
35、17

Fktは上記でインターフェースとして定義されています。記事全体を読んでください:-)
Alessandro Giusa '19 / 11/19

はい。EFunctionインターフェイスを名前としてFktに変更しました。タイプミスがありました。レビューありがとうございます:-)申し訳ありません。
アレッサンドロジューザ2017年

このようなコードを書くのは良い考えではないと思います...可能な場合は、jdkユーティリティを使用する必要があります。
ティルパンティルパン

0

値を保存したい場合:

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

0

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.