14

Guava のOptional パターンは、null のあいまいさを取り除くのに役立つため、優れています。このtransformメソッドは、チェーンの最初の部分が存在しない可能性がある場合に null セーフ メソッド チェーンを作成するのに非常に役立ちますが、チェーンの他の部分が存在しない場合は役に立ちません。

この質問は、変換が別の Optional を返すときに Guava Optional 型に関連しています。これは、本質的に同じ質問をしますが、意図された使用法ではない可能性がある別のユースケースについて質問しますOptional(エラーの処理)。

方法を検討してくださいOptional<Book> findBook(String id)findBook(id).transform(Book.getName)期待どおりに動作します。本が見つからない場合は を取得しAbsent<String>、本が見つかった場合は を取得しPresent<String>ます。

中間メソッドがnull/を返す一般的なケースでabsent()は、呼び出しを連鎖させる洗練された方法はないようです。たとえば、BookにメソッドOptional<Publisher> getPublisher()があり、ある本の出版社から出版されたすべての本を取得したいとします。自然な構文は のように見えますがfindBook(id).transform(Book.getPublisher).transform(Publisher.getPublishedBooks)transform(Publisher.getPublishedBooks)呼び出しが実際にはOptional<Optional<Publisher>>.

を返す関数を受け入れるtransform()ようなメソッドを onに持つことはかなり合理的です。関数の結果をオプションでラップしないことを除いて、現在の実装とまったく同じように動作します。( の) 実装は次のようになります。OptionalOptionalPresent

public abstract <V> Optional<V> optionalTransform(Function<? super T, Optional<V>> function) {
    return function.apply(reference);
}

の実装Absentは から変更されていませんtransform:

public abstract <V> Optional<V> optionalTransform(Function<? super T, Optional<V>> function) {
    checkNotNull(function);
    return Optional.absent();
}

また、従来のオブジェクトを操作するのでnullはなく、返されるメソッドを処理する方法があればいいのにと思います。Optionalこのようなメソッドは似ていますが、単に関数の結果をtransform呼び出します。Optional.fromNullable

Optional他の誰かがこの煩わしさに遭遇し、適切な回避策 (独自のクラスを作成する必要はありません) を見つけたかどうかに興味があります。また、Guava チームからの連絡や、この問題に関連するディスカッションについて教えていただければ幸いです (検索しても見つかりませんでした)。

4

2 に答える 2

12

Monad を探していますが、Guava の Optional (たとえば Scala の Option とは対照的に) は単なる Functor です。

Functor とは何ですか?!

Functor と Monad は一種のボックスであり、値をラップするコンテキストです。型 A の値を含む Functor は、関数 A => B を適用して結果を Functor に戻す方法を知っています。例: Optional から何かを取得し、変換して、Optional にラップします。関数型プログラミング言語では、このようなメソッドは「マップ」と呼ばれることがよくあります。

モナ…なに?

Monad は、Monad にラップされた値を返す関数を消費することを除いて、Functor とほとんど同じです (A => Monad、たとえば Int => Optional)。この魔法の Monad のメソッドは、しばしば「flatMap」と呼ばれます。

ここでは、基本的な FP 用語の本当に素晴らしい説明を見つけることができます: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html

ファンクターとモナドが登場!

Java 8 のオプションは、Functor ( http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#map-java.util.function.Function- ) と Monadの両方に分類できます。( http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#flatMap-java.util.function.Function- )。

素敵な mon(ad)olog さん、Marcin さん、どうすれば私の特定の問題を解決できますか?

私は現在、Java 6 を使用するプロジェクトに取り組んでおり、昨日、「Optionals」と呼ばれるヘルパー クラスを作成しました。これにより、多くの時間を節約できました。

Optional を Monads (flatMap) に変換できるヘルパー メソッドを提供します。

コードは次のとおりです: https://gist.github.com/mkubala/046ae20946411f80ac52

私のプロジェクトのコードベースはまだ null を戻り値として使用しているため、結果を Optional にラップするために使用できる Optionals.lift(Function) を導入しました。

結果を Optional に持ち上げるのはなぜですか? トランスフォームに渡された関数が null を返す可能性があり、式全体が「null の存在」を返す状況を回避するには (ちなみに、この事後条件のため、Guava の Optional ではこれは不可能です -> https://code. google.com/p/guava-libraries/source/browse/guava/src/com/google/common/base/Present.java?r=0823847e96b1d082e94f06327cf218e418fe2228#71 )。

いくつかの例

findEntity() が Optional を返し、Entity.getDecimalField(..) が BigDecimal または null を返す可能性があると仮定しましょう。

Optional<BigDecimal> maybeDecimalValue = Optionals.flatMap(
    findEntity(),
    new Function<Entity, Optional<BigDecimal>> () {
        @Override 
        public Optional<BigDecimal> apply(Entity input) {
            return Optional.fromNullable(input.getDecimalField(..));
        }
    }
);

さらに別の例として、エンティティから 10 進数値を抽出し、null を返す可能性のある関数が既にあると仮定します。

Function<Entity, Decimal> extractDecimal = .. // extracts decimal value or null
Optional<BigDecimal> maybeDecimalValue = Optionals.flatMap(
    findEntity(),
    Optionals.lift(extractDecimal)
);

そして最後になりましたが、例としてのユースケース:

Optional<Publisher> maybePublisher = Optionals.flatMap(findBook(id), Optionals.lift(Book.getPublisher));

// Assuming that getPublishedBooks may return null..
Optional<List<Book>> maybePublishedBooks = Optionals.flatMap(maybePublisher, Optionals.lift(Publisher.getPublishedBooks));

// ..or simpler, in case when getPublishedBooks never returns null
Optional<List<Book>> maybePublishedBooks2 = maybePublisher.transform(Publisher.getPublishedBooks);

// as a one-liner:
Optionals.flatMap(maybePublisher, Optionals.lift(Publisher.getPublishedBooks)).transform(Publisher.getPublishedBooks);
于 2014-06-25T23:35:52.483 に答える
0

あなたはおそらくそれを理解しましたが、.or(Optional.absent)を返すすべての変換の後に追加できますOptional(あなたの場合は の後.transform(Book.getPublisher)に還元Optional<Optional<T>>Optional<T>ます:

Optional<List<Book>> publishedBooks = findBook(id).transform(Book.getPublisher).
        or(Optional.absent()).transform(Publisher.getPublishedBooks);

残念ながら、Optional.absentここでは の型を推測できないため、コードは実際には次のようになります。

Optional<List<Book>> publishedBooks = book.transform(Book.getPublisher).
        or(Optional.<Publisher> absent()).transform(Publisher.getPublishedBoooks);

あまり便利ではありませんが、他に方法はないようです。

于 2013-10-26T13:39:37.717 に答える