web-dev-qa-db-ja.com

変数のnullチェックよりもオプションの優先を使用するのはなぜですか?

次の2つのコード例を見てください。

if(optional.isPresent()) {
    //do your thing
}

if(variable != null) {
    //do your thing
}

最も明白な違いがわかる限り、オプションは追加のオブジェクトを作成する必要があります。

しかし、多くの人々がオプションを急速に採用し始めました。 nullチェックに対してオプションを使用する利点は何ですか?

25
William Dunne

Optionalは型システムを利用して、他の方法では頭の中ですべて行う必要がある作業を実行します。つまり、指定された参照がnullであるかどうかを記憶します。これはいい。コンパイラーに退屈なドラッグワークを処理させ、創造的で興味深い作業のために人間の思考を確保することは常に賢い方法です。

Optionalがない場合、コード内のすべての参照は不発弾のようなものです。それへのアクセスは何か有用なことをするかもしれません、さもなければそれはあなたのプログラムを例外で終了させるかもしれません。

Optionalあり、nullなしの場合、通常の参照へのすべてのアクセスは成功し、Optionalへのすべての参照は、設定が解除されていないか確認しない限り成功します。それは保守性の大きな勝利です。

残念ながら、現在Optionalを提供するほとんどの言語はnullを廃止していないため、「絶対にnullは絶対に使用しない」という厳密なポリシーを制定することによってのみ、コンセプトから利益を得ることができます。 。したがって、Optionalなど。 Javaは、理想的であるほど魅力的ではありません。

20
Kilian Foth

Optionalは、他の回答がカバーしているように、失敗する可能性のある操作により強力なタイピングをもたらしますが、これはfarが最も興味深いまたは価値のあるものからOptionalsがテーブル。はるかに便利なのは、失敗のチェックを遅らせたり、回避したり、失敗する可能性のある多くの操作を簡単に構成したりする機能です。

例のコードからoptional変数があった場合、それぞれが失敗する可能性がある2つの追加手順を実行する必要があったことを考慮してください。途中でいずれかのステップが失敗した場合は、代わりにデフォルト値を返します。 Optionalsを正しく使用すると、次のような結果になります。

_return optional.flatMap(x -> x.anotherOptionalStep())
               .flatMap(x -> x.yetAnotherOptionalStep())
               .orElse(defaultValue);
_

nullを使用すると、続行する前にnullを3回チェックする必要があり、コードに多くの複雑さとメンテナンスの頭痛が加わります。 Optionalsには、flatMapおよびorElse関数にそのチェックが組み込まれています。

isPresentを1回呼び出さなかったことに注意してください。これは、Optionalsを使用するときにコードのにおいだと考える必要があります。これは必ずしもneverisPresentを使用する必要があることを意味するわけではありません。より良い方法があるかどうかを確認するために、実行するコードを徹底的に精査する必要があるだけです。そうでなければ、あなたは正しいです、あなたはnullを使用するよりもわずかな型安全上の利益を得ているだけです。

また、コードの他の部分をnullポインターから中間結果から保護するために、これをすべて1つの関数にカプセル化することについてはそれほど心配していません。たとえば、別の関数に.orElse(defaultValue)を含める方が理にかなっている場合、そこに配置することについてのクォームがはるかに少なく、必要に応じて異なる関数間の操作を構成する方がはるかに簡単です。

19
Karl Bielefeldt

有効な応答としてnullが発生する可能性があることを強調しています。この応答は、(正しくまたは誤って)返されないことがよくあります。 nullが有効であるときに数回ハイライト表示すると、無意味なnullチェックの膨大な数でコードを散らかすことを省略できます。

理想的には、変数をnullに設定すると、オプション以外の場所ではコンパイル時エラーになります。実行時のnullポインタ例外を排除します。明らかに、後方互換性はこれを排除し、悲しいことに

10
Richard Tingle

例を挙げましょう。

class UserRepository {
     User findById(long id);
}

この方法が何を意図しているのかは非常に明確です。しかし、リポジトリに指定されたIDのユーザーが含まれていない場合はどうなりますか?例外をスローするか、nullを返しますか?

2番目の例:

class UserRepository {
     Optional<User> findById(long id);
}

ここで、指定されたIDのユーザーがいない場合はどうなりますか?そうです、あなたはOptional.absent()インスタンスを取得し、それをOptional.isPresent()で確認できます。オプション付きのメソッドシグネチャは、そのコントラクトについてより明確です。メソッドがどのように動作するかがすぐにわかります。

また、クライアントコードを読み取る際にも利点があります。

User user = userRepository.findById(userId).get();

.get()を即時のコード臭として見るように訓練しました。これは、クライアントが呼び出されたメソッドのコントラクトを意図的に無視することを意味します。この場合、呼び出されたメソッドのコントラクトが何であるか(すぐにnullが許可されるかどうかにかかわらず)がすぐにわからないため、最初に確認する必要があるため、これはOptionalがない場合よりもはるかに簡単です。

Optionalは、戻り値の型がOptionalのメソッドは決してnullを返さないという規則で使用され、通常、Optionalの戻り値型のないメソッドはnullを返さない(それほど強くない)規則も使用します(ただし、@ Nonnull、@ NotNullなどで注釈を付けることをお勧めします)。 。

4
qbd

他の回答に加えて、クリーンなコードを記述する場合のOptionalのその他の主な利点は、次に示すように、値が存在する場合にLambda式を使用できることです。

opTr.ifPresent(tr -> transactions.put(tr.getTxid(), tr));
3
William Dunne

_Optional<T>_を使用すると、メソッドの有効な応答/戻り値として「失敗」または「結果なし」を使用できます(たとえば、データベースルックアップなど)。 Optionalを使用する代わりにnullを使用して失敗/結果がないことを示すと、いくつかの利点があります。

  • 「失敗」isオプションであることを明確に伝えます。メソッドのユーザーは、nullmightが返されるかどうかを推測する必要はありません。
  • nullは、少なくとも私の意見では、セマンティクスが完全に明確ではない可能性があるため、何かを示すためにnotを使用する必要があります。以前に参照されたオブジェクトが収集される可能性があることをガベージコレクターに伝えるために使用する必要があります。
  • Optionalクラスには、値を条件付きで処理する(ifPresent()など)、または透過的な方法でデフォルト値を定義する(orElse())ための多くのNiceメソッドが用意されています。

残念ながら、nullオブジェクト自体はまだOptionalである可能性があるため、コードでのnullチェックの必要性を完全に取り除くことはできません。

3
pansen

次の方法を検討してください。

void dance(Person partner)

次に、いくつかの呼び出しコードを見てみましょう。

dance(null);

danceはnullを予期していなかったため、これは他のどこかでクラッシュを追跡することを潜在的に困難にします。

dance(optionalPerson)

これは、ダンスがOptional<Person>ではなくPersonを期待していたため、コンパイルエラーが発生します。

dance(optionalPerson.get())

人物がいない場合は、この行で例外が発生します。私がOptional<Person>を不正に人物に変換しようとしたところです。重要なのは、nullを渡すのとは異なり、exceptはプログラムの他の場所ではなく、ここまでトレースすることです。

まとめると、オプションを誤用すると問題の追跡が容易になり、nullを誤用すると問題の追跡が困難になります。

2
Winston Ewert