オプションの用途


271

6か月以上Java 8を使用しているので、新しいAPIの変更にかなり満足しています。私がまだ確信していない領域の1つは、いつ使用するかOptionalです。どこにでもそれを使いたいのかnull、どこにも使いたくないのか、どんどん変わっていくようです。

私がそれを使用できる状況はたくさんあるようで、それが利点(読みやすさ/ nullの安全性)を追加するのか、それともオーバーヘッドを増やすだけなのか、私にはわかりません。

だから、私はいくつかの例を持っています、そして私はコミュニティOptionalが有益であるかどうかについての考えに興味があります。

1-メソッドが返すことができるパブリックメソッドの戻り値の型としてnull

public Optional<Foo> findFoo(String id);

2-パラメータが次の場合のメソッドパラメータとしてnull

public Foo doSomething(String id, Optional<Bar> barOptional);

3-Beanのオプションのメンバーとして:

public class Book {

  private List<Pages> pages;
  private Optional<Index> index;

}

4-でCollections

一般的に私は思いません:

List<Optional<Foo>>

何かを追加します-特に値のfilter()削除nullなどに使用できますがOptional、コレクションでの良い使用法はありますか?

私が見逃したケースはありますか?


2
たとえば、代替マップがある場合に便利です。例えばMap<Character, String>。代替がない場合は、これを使用できますOptional.ofNullable(map.get(c)).orElse(String.valueOf(c))。また、オプションは、グアバから盗まれたことに注意して、それが非常に良く構文を持っていますOptional.fromNullable(map.get(c)).or(String.valueOf(c));
FGE

3
また、コレクションには、null値を許可しないコレクションもあります。オプションはここの請求書に適合します。そして、あなたは.filter(Optional::absent)「ヌル値」を出すことができます
fge 2014年

2
@fge公平に言えば、オプションの概念は実際にはFPに由来していると思います。
VH-NZZ 2014年

2
@fgeはそれでよりよく表現されていgetOrDefault()ませんか?
ジムギャリソン

回答:


206

の主要なポイントはOptional、関数が値を返すための手段を提供して、戻り値がないことを示すことです。こちらのディスカッションをご覧ください。これにより、呼び出し元は一連の滑らかなメソッド呼び出しを継続できます。

これは、OPの質問のユースケース#1に最もよく一致します。ただし、のようなものはnullを返すことができないため、値ないことはnullよりも正確な定式化ですIntStream.findFirst

ユースケース#2では、オプションの引数をメソッドに渡すと、これを機能させることができますが、かなり不格好です。文字列の後にオプションの2番目の文字列が続くメソッドがあるとします。Optional2番目の引数としてを受け入れると、次のようなコードが生成されます。

foo("bar", Optional.of("baz"));
foo("bar", Optional.empty());

nullを受け入れることもできます。

foo("bar", "baz");
foo("bar", null);

おそらく最良の方法は、単一の文字列引数を受け入れ、2番目の引数にデフォルトを提供するオーバーロードメソッドを用意することです。

foo("bar", "baz");
foo("bar");

これには制限がありますが、上記のどちらよりもはるかに優れています。

ユースケース#3及び#4を有する、Optionalクラスフィールドまたはデータ構造では、APIの誤用であると考えられます。最初に、それはOptional上で述べたようにの主な設計目標に反します。第二に、値を追加しません。

に値が存在しない場合の対処方法にOptionalは、代替値を提供する方法、関数を呼び出して代替値を提供する方法、または例外をスローする方法の3つがあります。フィールドに格納する場合は、初期化時または割り当て時にこれを行います。リストに値を追加する場合は、OPで述べたように、値を追加しないという選択肢があり、それにより、存在しない値を「フラット化」します。

誰かがOptionalフィールドまたはコレクションに本当にを格納したいという偶発的なケースを思いつく可能性があると私は確信していますが、一般的には、これを行わないことが最善です。


47
#3に同意しません。Optional値の有無に応じて、処理を行うか行わないかをフィールドとして指定すると便利です。
glglgl 2015年

15
if(foo == null)フィールドを単にとして保存するより内部で行う方が良い理由はわかりませんoptional。そしてgetOptionalFoo()、フィールドを単にとして保存するよりも、内部的に明示的に呼び出す方が良い理由はわかりませんOptional。また、1つのフィールドが可能でnullあり、1つのフィールドが不可能である場合はnull、それらを作成するOptionalかどうかで、コンパイラーにそれを伝えてくださいOptional
モグロナロール2016年

27
@StuartMarks、これは、すべてのOptional<>専門家から聞いたときに私を困惑させるものの1つですOptional<>。「フィールドに保管しないでください」。私は本当にこの勧告のポイントを理解しようとしています。ノードが親を持つことができるかどうかに関係なく、DOMツリーについて考えます。Node.getParent()返されるのはユースケース内のようOptional<Node>です。を保存してnull毎回結果をラップすることを本当に期待していますか?どうして?私のコードを醜く見せること以外に、この余分な非効率性は私を何に買いますか?
Garret Wilson

7
@GarretWilson一方、あなたが持っているとしますOptional<Node> getParent() { return Optional.ofNullable(parent); }。毎回割り当てるため、これ高価に見えますOptional!ただし、呼び出し元がすぐにアンパックした場合、オブジェクトは非常に短命で、エデンスペースから昇格されることはありません。これは、JITのエスケープ分析によって削除される可能性もあります。一番下の答えはいつものように「依存する」ですがOptional、フィールドで使用するとメモリ使用量が膨らみ、データ構造のトラバースが遅くなります。最後に、コードが雑然としていると思いますが、これは好みの問題です。
スチュアートマーク

6
@GarretWilsonでは、そのためにブログを書きましたか?私はそれに興味があります:)
akhil_mittal

78

私はゲームに遅れましたが、それだけの価値があるので、2セントを追加したいと思います。彼らはの設計目標にOptional反しています。これはスチュアートマークスの回答で十分に要約されていますが、私はまだ(明らかに)その有効性を確信しています。

どこでもオプションを使用

一般に

私は使用についてブログ投稿Optional全体を書きましたが、それは基本的にこれに帰着します:

  • 可能な限り可能な限りオプションを回避するようにクラスを設計する
  • 残りのすべてのケースでは、デフォルトではOptionalなくnull
  • おそらく例外を作る:
    • ローカル変数
    • プライベートメソッドに値と引数を返す
    • パフォーマンスが重要なコードブロック(推測なし、プロファイラーを使用)

最初の2つの例外により、での参照のラップとアンラップのオーバーヘッドの認識を減らすことができOptionalます。それらは、ヌルがインスタンス間で境界を合法的に渡すことができないように選択されます。

これは、Optionalsと同じくらい悪いコレクションでsを許可することはほとんどありませんnull。しないでください。;)

ご質問について

  1. はい。
  2. オーバーロードがオプションでない場合、はい。
  3. 他のアプローチ(サブクラス化、装飾、...)が選択肢がない場合、はい。
  4. いやいや!

メリット

これを行うnullと、コードベース内のの存在が減少しますが、根絶はしません。しかし、それは重要なポイントではありません。他にも重要な利点があります。

意図を明確にする

を使用Optionalすると、変数がオプションであることを明確に表現できます。コードのリーダーやAPIのコンシューマーは、そこに何もない可能性があり、値にアクセスする前にチェックが必要であるという事実に頭を打たれます。

不確実性を取り除く

発生Optionalの意味がなければnull不明確です。状態の正当な表現(を参照Map.get)、または初期化の欠落または失敗などの実装エラーの可能性があります。

これは、の永続的な使用によって劇的に変化しますOptional。ここで、の発生はすでにnullバグの存在を示しています。(値の欠落が許可されている場合は、Optionalが使用されていたためです。)これにより、nullポインター例外のデバッグが容易になりnullます。

より多くのヌルチェック

nullもはや何もすることができないので、これはどこでも強制することができます。注釈、アサーション、プレーンチェックのいずれの場合でも、この引数またはその戻り値の型がnullになる可能性があるかどうかについて考える必要はありません。できません!

短所

もちろん、特効薬はありません...

パフォーマンス

値(特にプリミティブ)を追加のインスタンスにラップすると、パフォーマンスが低下する可能性があります。タイトなループでは、これは顕著になるか、さらに悪化する可能性があります。

コンパイラーは、Optionalsの存続期間が短い場合の余分な参照を回避できる場合があることに注意してください。Java 10では、値型によってペナルティがさらに軽減または削除される可能性があります。

連載

Optionalはシリアライズ可能ではありませんが、回避策は過度に複雑ではありません。

不変性

Javaのジェネリック型の不変性のため、実際の値型がジェネリック型引数にプッシュされると、特定の操作が煩雑になります。ここでは例を示します(「パラメトリック多態性」を参照)


もう1つの欠点(または制限)は、Optional(xxx)インスタンスをソートできないことですが、Oracleはその制限を意図的に明確に課しています。これらのクラスを特定の状況でのみ使用する必要がある場合に利用できるようにし、開発者が正しいことを行うと想定していることについて、私は懐疑的です。1つの問題を解決すると、別の問題が発生する可能性があります。多分それが本当に意図された使用法ではない場合、「望ましくない」方法(例えば、メソッドパラメータとして、コレクション、コンストラクタなど)でOptional(xxx)クラスを使用する場合、開発者を支援するコンパイラ警告があるはずです。
skomisa 2015

約4、オプションのコレクション:実際にオプションをLists に格納すると便利な場合があります。これは、特定の要素が特定のインデックスに配置されることが重要である場合に当てはまりますが、要素は存在する場合と存在しない場合があります。それはList<Optional<T>>タイプによって明確に伝えられます。一方、いくつかのコレクションのメンバーであるオブジェクトのみが重要である場合は、意味がありません。
Lii

3
ユースケース#2はまだ非常に疑わしいと思います。Optional方法することができに渡されますnull。それであなたは何を得ましたか?これで、2つのチェックを行うことができます。参照してくださいstackoverflow.com/a/31923042/650176
デヴィッド・V

2
@DavidVが私が与えた利点のリストを見てください-それらはすべてその場合にも当てはまります。その上、あなたがすべてに行くOptionalなら(あなたがそれを引数として渡そうと思っているならそうであるべきです)null、決して正当な値ではありません。したがって、明示的なパラメーターを指定してメソッドを呼び出すのはnull間違いです。
Nicolai

28

個人的には、IntelliJのコードインスペクションツールを使用@NotNullして@Nullableチェックすることをお勧めします。これらは主にコンパイル時間であるため(実行時チェックが必要になる場合があります)、コードの可読性と実行時パフォーマンスのオーバーヘッドが低くなります。Optionalを使用するほど厳密ではありませんが、この厳密さの欠如は、まともな単体テストによって裏付けられます。

public @Nullable Foo findFoo(@NotNull String id);

public @NotNull Foo doSomething(@NotNull String id, @Nullable Bar barOptional);

public class Book {

  private List<Pages> pages;
  private @Nullable Index index;

}

List<@Nullable Foo> list = ..

これはJava 5で動作し、値をラップおよびアンラップする必要はありません。(またはラッパーオブジェクトを作成します)


7
うーん、それらはIDEAアノテーションですか?私は@Nonnull個人的にはJSR 305を好みます
-FindBugsで

@fgeこの点で標準が不足していますが、ツールは一般に構成可能であり、最終的には必要がないと思います@Nonnull @NotNull etc
Peter Lawrey

4
はい、そうです; IDEA(13.x)はMEHは、私はいつもanywa JSR 305を使用して終了... 3さまざまな選択肢を提供します
FGE

3
私はこれが古いですけど、注釈&ツールについて議論がへのリンクに値するstackoverflow.com/questions/35892063/... -ところで、特にJavaの8の場合を扱う
ステファン・ヘルマン

25

私はGuava Optionalと彼らのwikiページがそれをかなりうまく入れていると思います:

nullに名前を付けることによる可読性の向上に加えて、Optionalの最大の利点は、ばかであるという証拠です。Optionalを積極的にアンラップしてそのケースに対処する必要があるため、プログラムをコンパイルしたい場合は、不在のケースについて積極的に考える必要があります。Nullを使用すると、物事を簡単に忘れることが邪魔になりやすくなります。FindBugsは役立ちますが、この問題にはほとんど対応していないと考えられます。

これは、「存在」する場合としない場合がある値を返す場合に特に関係があります。あなた(と他の人)は、other.method(a、b)がnull値を返す可能性があることを忘れる可能性がはるかに高いです。Optionalを返すと、コードをコンパイルするためにオブジェクト自体をアンラップする必要があるため、呼び出し元がそのケースを忘れることができなくなります。-(出典:Guava Wiki-nullの使用と回避-ポイントは何ですか?

Optionalオーバーヘッドが多少追加されますが、その明らかな利点は 、オブジェクトが存在しない可能性があることを明示的に示し、プログラマーが状況を処理するように強制することです。それは誰かが最愛の!= nullチェックを忘れることを防ぎます。

2の例をとると、次のように書く方がはるかに明示的なコードだと思います。

if(soundcard.isPresent()){
  System.out.println(soundcard.get());
}

より

if(soundcard != null){
  System.out.println(soundcard);
}

私にとってOptionalは、サウンドカードが存在しないという事実をよりよく捉えています。

あなたのポイントについての私の2¢:

  1. public Optional<Foo> findFoo(String id);-よくわかりません。たぶん私は戻ってくるResult<Foo>かもしれませんどの空をか含まれていFoo。これは同様の概念ですが、実際にはありませんOptional
  2. public Foo doSomething(String id, Optional<Bar> barOptional);-Peter Lawreyの回答のように、@ Nullableとfindbugsチェックを優先します。このディスカッションも参照してください。
  3. あなたの本の例-オプションを内部で使用するかどうかはわかりませんが、それは複雑さに依存する可能性があります。本の「API」については、を使用しOptional<Index> getIndex()て、本に索引がない可能性があることを明示的に示します。
  4. コレクションでは使用せず、コレクションでnull値を許可しない

一般的に、nullsの通過を最小限に抑えるようにします。(一度燃焼したら...)適切な抽象化を見つけて、特定の戻り値が実際に何を表しているかを他のプログラマーに示すことは価値があると思います。


15
あなたは書くでしょうsoundcard.ifPresent(System.out::println)。呼び出しisPresentは、意味的にはの確認と同じですnull
より良いオリーブ

私にとっての問題は、オプションの型を持つものはまだnullである可能性があることです。したがって、実際に完全に安全であるためには、次のようなことを行う必要がありますif(soundcard != null && soundcard.isPresent())。Optionalを返し、nullを返すこともできるAPIを作成することは、誰も行わないことを願っています。
Simon Baumgardt-Wellander 2016

「プログラムをコンパイルしたい場合は、オプションのラップを積極的に解除してそのケースに対処する必要があるため、この欠落したケースについて積極的に考える必要があります。」Javaでの作業はそれほど多くありませんが、これがどのように強制されるかは本当にわかりません。Javaがアンラップを強制し、コンパイルするために!isPresentケースをチェックすることを強制するために何をするか説明できますか?
クラッシュ

15

Oracleチュートリアルから:

Optionalの目的は、コードベースのすべてのnull参照を置き換えるのではなく、メソッドのシグネチャを読み取るだけで、ユーザーがオプションの値を期待するかどうかを判断できる、より優れたAPIの設計を支援することです。さらに、Optionalは値の不在に対処するためにOptionalを積極的にアンラップすることを強制します。その結果、意図しないnullポインター例外からコードを保護します。


3
メソッドの戻り部分のためのAPIですよね?型消去のため、オプションはパラメーターとして使用すべきではありませんか?null可能パラメーターから変換したり、nullコンストラクターパラメーターからフィールドを初期化したりするときに、メソッド内でOptionalを使用する場合があります...それをnullのままにするよりも便利かどうかを考えようとしています-誰かが何か考えがありますか?
ycomp

@ycompこれらのケースについては、この質問またはその質問でかなり多くの情報を見つけることができます。これらの質問への回答は、質問された内容よりもはるかに多くを伝え、他のケースをカバーします。詳細については、詳細を参照するか、独自の質問をしてください。おそらく、ここではあまり注目されないでしょう。; )
ctomek 2016

8

1-メソッドがnullを返す可能性がある場合のパブリックメソッドの戻り値の型として:

以下は、ユースケース#1の有用性を示す優れた記事です。そこでこのコード

...
if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        Country country = address.getCountry();
        if (country != null) {
            String isocode = country.getIsocode();
            isocode = isocode.toUpperCase();
        }
    }
}
...

これに変換されます

String result = Optional.ofNullable(user)
  .flatMap(User::getAddress)
  .flatMap(Address::getCountry)
  .map(Country::getIsocode)
  .orElse("default");

Optionalをそれぞれのゲッターメソッドの戻り値として使用する。


2

ここに興味深い使い方があります(私は信じています)...テスト。

私は自分のプロジェクトの1つを徹底的にテストするつもりなので、アサーションを作成します。確認する必要のあるものと確認しないものがあります。

したがって、以下のように、アサートするものを構築し、アサートを使用してそれらを検証します。

public final class NodeDescriptor<V>
{
    private final Optional<String> label;
    private final List<NodeDescriptor<V>> children;

    private NodeDescriptor(final Builder<V> builder)
    {
        label = Optional.fromNullable(builder.label);
        final ImmutableList.Builder<NodeDescriptor<V>> listBuilder
            = ImmutableList.builder();
        for (final Builder<V> element: builder.children)
            listBuilder.add(element.build());
        children = listBuilder.build();
    }

    public static <E> Builder<E> newBuilder()
    {
        return new Builder<E>();
    }

    public void verify(@Nonnull final Node<V> node)
    {
        final NodeAssert<V> nodeAssert = new NodeAssert<V>(node);
        nodeAssert.hasLabel(label);
    }

    public static final class Builder<V>
    {
        private String label;
        private final List<Builder<V>> children = Lists.newArrayList();

        private Builder()
        {
        }

        public Builder<V> withLabel(@Nonnull final String label)
        {
            this.label = Preconditions.checkNotNull(label);
            return this;
        }

        public Builder<V> withChildNode(@Nonnull final Builder<V> child)
        {
            Preconditions.checkNotNull(child);
            children.add(child);
            return this;
        }

        public NodeDescriptor<V> build()
        {
            return new NodeDescriptor<V>(this);
        }
    }
}

NodeAssertクラスでは、次のようにします。

public final class NodeAssert<V>
    extends AbstractAssert<NodeAssert<V>, Node<V>>
{
    NodeAssert(final Node<V> actual)
    {
        super(Preconditions.checkNotNull(actual), NodeAssert.class);
    }

    private NodeAssert<V> hasLabel(final String label)
    {
        final String thisLabel = actual.getLabel();
        assertThat(thisLabel).overridingErrorMessage(
            "node's label is null! I didn't expect it to be"
        ).isNotNull();
        assertThat(thisLabel).overridingErrorMessage(
            "node's label is not what was expected!\n"
            + "Expected: '%s'\nActual  : '%s'\n", label, thisLabel
        ).isEqualTo(label);
        return this;
    }

    NodeAssert<V> hasLabel(@Nonnull final Optional<String> label)
    {
        return label.isPresent() ? hasLabel(label.get()) : this;
    }
}

つまり、アサートは、ラベルを確認する場合にのみトリガーされます。


2

Optionalクラスを使用するnullと、使用を避けてより良い代替手段を提供できます。

  • これにより、開発者は、キャッチされていないものを回避するために存在を確認することができますNullPointerException

  • 欠落している可能性のある値をどこに期待できるかを確認できるため、APIのドキュメント化が向上します。

Optionalオブジェクトをさらに処理するための便利なAPIを提供します isPresent()get(); orElse(); orElseGet(); orElseThrow(); map(); filter(); flatmap()

さらに、多くのフレームワークはこのデータ型を積極的に使用し、APIから返します。


2

Javaでは、関数型プログラミングに夢中でなければ、これらを使用しないでください。

それらはメソッドの引数として場所がありません(私は誰かが空のオプションだけでなく、nullオプションをあなたに渡すことを約束します)。

これらは戻り値には意味がありますが、クライアントクラスに動作構築チェーンを引き延ばしてもらいます。

FPとチェーンは、Javaのような命令型言語ではほとんど機能しません。これは、読むだけでなくデバッグも非常に困難にするためです。行に移動すると、プログラムの状態も意図もわかりません。あなたはそれを理解するためにステップインする必要があります(ステップフィルターにもかかわらず多くのスタックフレームが深いコードではありません)そしてあなたが追加したコード/ラムダで停止できることを確認するためにたくさんのブレークポイントを追加する必要があります、単純にif / else / callの簡単な行を歩くのではなく、

関数型プログラミングが必要な場合は、Java以外のものを選択し、それをデバッグするためのツールがあることを願っています。


1

Optionalは、null値を返す可能性のあるメソッドの一般的な代替物ではないと思います。

基本的な考え方は次のとおりです。値がなくても、将来その値が使用できる可能性があるという意味ではありません。これは、findById(-1)とfindById(67)の違いです。

発信者にとってのオプションの主な情報は、与えられた値に頼ることはできないかもしれませんが、いつか利用できる可能性があるということです。多分それは再び消え、後でまた戻ってくるでしょう。オン/オフスイッチのようなものです。ライトのオン/オフを切り替える「オプション」があります。ただし、スイッチをオンにするライトがない場合、オプションはありません。

したがって、以前はnullが返される可能性があったすべての場所にOptionalsを導入するのは面倒すぎると思います。引き続きnullを使用しますが、ツリーのルート、遅延初期化、明示的な検索メソッドなどの制限された領域でのみ使用します。


0

思われるOptionalオプションのtype Tがプリミティブ型のような場合にのみ有効ですintlongchar「本物」のクラスについてなど、あなたが使用できるように、それは私には意味がありません。nullとにかく値を。

私はそれがここから(または別の同様の言語概念から)取られたと思います。

Nullable<T>

C#では、これNullable<T>はずっと前に値型をラップするために導入されました。


2
これは、グアバのの盗作であるOptionalという事実で
FGE

2
@fge OK、でもこれがC#(2005、MS.NET 2.0)にあったとき、グアバはなかったと思います。そして... C#がこれをどこから取得したかを知っている人。
peter.petrov

3
@fge「グアバのオプションのリポフ」は、グアバの人たちがディスカッションに参加し、経験を提供し、一般的には賛成していたので、ちょっと面白い方法ですjava.util.Optional
スチュアートマーク

6
@fge JavaのAPIがGuavaのAPIに強く影響されたことは間違いありません。盗むように聞こえる「詐欺」について問題を抱えています。グアバの連中は貢献したJava 8に貴重なアイデアの多くを
スチュアート・マークス

6
あなたは要点を逃しています。nullを返す代わりにオプションが使用されます。NullPointerExceptionをスローする可能性よりも安全です。参照:oracle.com/technetwork/articles/java/…–キリゾ15
1

0

は、Iteratorデザインパターンの変更不可能なインスタンスと同様のセマンティクスがありますOptional

  • オブジェクトを参照する場合としない場合があります(で指定isPresent()
  • get()オブジェクトを参照している場合は、(を使用して)間接参照できます
  • ただし、シーケンスの次の位置に進めることはできません(next()メソッドはありません)。

したがって、Optional以前にJavaの使用を検討したことがあるコンテキストで、を返すか渡すことを検討してくださいIterator


-1

Java SE 8では、java.util.Optionalという新しいクラスが導入されています。

空のオプションまたはnull値を持つオプションを作成できます。

Optional<String> emptyOptional = Optional.empty(); 

そして、これは非null値を持つオプションです:

String valueString = new String("TEST");
Optional<String> optinalValueString = Optional.of(valueString );

値が存在する場合に何かを行う

これでOptionalオブジェクトができたので、値の有無を明示的に処理するために使用できるメソッドにアクセスできます。次のように、nullチェックを忘れずに行う必要があります。

String nullString = null;
if (nullString != null) {
    System.out.println(nullString);
}

次のように、ifPresent()メソッドを使用できます。

Optional<String> optinalString= null;
optinalString.ifPresent(System.out::println);

package optinalTest;

import java.util.Optional;

public class OptionalTest {
    public Optional<String> getOptionalNullString() {
        return null;
//      return Optional.of("TESt");
    }

    public static void main(String[] args) {

        OptionalTest optionalTest = new OptionalTest();

        Optional<Optional<String>> optionalNullString = Optional.ofNullable(optionalTest.getOptionalNullString());

        if (optionalNullString.isPresent()) {
            System.out.println(optionalNullString.get());
        }
    }
}

このコピーペーストは安価な評判を得ることを意図したものでしたが、OPの質問とは関係がありませんでした
スティンガー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.