Java 8ゲッターはオプションの型を返す必要がありますか?


288

Optional Java 8で導入された型は、多くの開発者にとって新しいものです。

Optional<Foo>クラシックの代わりにgetterメソッドが型を返すのFooは良い習慣ですか?値はであると想定しますnull


8
これは有権者の回答を集める可能性が高いですが、それは良い質問です。この件についての真実を含む回答を楽しみにしています。
ジャスティン

8
問題は、ヌルブリットが避けられないかどうかです。コンポーネントにはnullを許可されているプロパティがある場合がありますが、そのコンポーネントを使用するプログラマは、そのプロパティを厳密に保持しないことを決定する場合がありますnull。したがって、プログラマーはそのときに対処する必要はありませんOptional。または、言い換えれば、null検索の結果(Optional適切な場合)のように、値が存在しないことを実際に表しているか、またはnull可能な値のセットの1つのメンバーにすぎません。
Holger

1
@NotNullアノテーションに関するディスカッションも参照してください:stackoverflow.com/q/4963300/873282
koppor 2017

回答:


515

もちろん、人々は自分がやりたいことをします。しかし、私たちはこの機能を追加するときに明確な意図を持っていました、そしてそれは多くの人々がそうすることを望んでいたであろうのと同じくらい一般的な目的の多分タイプではありませんでした。私たちの意図は、「結果なし」を表す明確な方法が必要なライブラリメソッドの戻り値の型に限定的なメカニズムを提供することであり、そのために使用nullすると圧倒的にエラーが発生する可能性がありました。

たとえば、結果の配列または結果のリストを返すようなものには使用しないでください。代わりに、空の配列またはリストを返します。何かのフィールドやメソッドパラメータとして使用することはほとんどありません。

ゲッターの戻り値として日常的に使用することは間違いなく使いすぎだと思います。

Optionalにはそれを避けるべきであるということは何の問題もありません。それは多くの人々が望んでいたことではありません。したがって、私たちは熱心な使いすぎのリスクをかなり心配していました。

(公共サービスの告知:NEVER呼び出していないOptional.getあなたはそれがnullになることはありませんことを証明できない限り、代わりのような安全な方法のいずれかを使用orElseまたはをifPresent振り返ってみると、私たちが求めているはずです。get何かのようにgetOrElseThrowNoSuchElementException、これは非常に危険な方法であったことや、それがはるかに明確に作ら何かこれOptionalは、そもそも全体の目的を損なうことになります。教訓(更新:Java 10にはOptional.orElseThrow()、意味的にと同等ですがget()、その名前がより適切です。))


24
(最後の部分について)...そして、我々は値が決してされていることを、自信を持っているnull私たちが使用していないかもしれないorElseThrow(AssertionError::new)、エヘンかorElseThrow(NullPointerException::new)...
ホルガー・

25
もしあなたが多目的または多目的の汎用タイプを導入することを意図していたなら、あなたは何を違った方法でしたか?Optionalが1つの目的に合わない方法はありますか、それとも、新しいAPI全体にOptionalを導入すると、Java風ではなくなるというだけのことですか?
David Moles、

22
「汎用型」とは、それを近似するライブラリクラスを提供するのではなく、言語の型システムに組み込むことを意味しました。(一部の言語にはT?(Tまたはnull)およびT!(null不可T)の型があります)オプションは単なるクラスです。言語サポートの場合のように、FooとOptional <Foo>の間で暗黙的な変換を行うことはできません。
Brian Goetz、2015

11
しばらくの間、Javaの世界ではオプションが重視されており、静的分析の方が優れていなかったのではないかと思っていました。オプションにはいくつかの利点がありますが、大きな利点nullは後方互換性です。Map::getnullableを返します。は返しVませんOptional<V>。これは決して変更されません。@Nullableただし、簡単に注釈を付けることができます。今、私たちは持っている2つのであることには本当に悪いことのように思える位置起こって静的解析を、取得の値不足-、プラス少ないインセンティブを表現する方法を。
yshavit

21
StackOverflowでこの回答を読むまで、それをプロパティの戻り値として使用することが意図したものではないことを知りませんでした。実際に、私はそれはy'allのは、OracleのWebサイト上でこの記事を読んだ後に意図を正確に何をしたと信じてリードした: oracle.com/technetwork/articles/java/... 明確化をありがとう
ジェイソン・トンプソン

73

私自身の少しの調査を行った後、これが適切な場合に示唆されるかもしれない多くの事柄に出くわしました。最も信頼できるのは、Oracleの記事からの次の引用です。

「Optionalクラスの意図は、すべてのnull参照を置き換えることでないことに注意することが重要です。代わりに、その目的は、より理解しやすいAPIを設計し、メソッドのシグネチャを読み取るだけで、オプションの値を期待できます。これにより、値の不在に対処するためにオプションを積極的にアンラップする必要があります。」- Nullポインタ例外のうんざりしていませんか?Java SE 8のオプションの使用を検討してください!

また、Java 8からのこの抜粋も見つかりました。オプション:使用方法

「オプションは、私たちに何も購入しないため、これらのコンテキストで使用することを意図していません。

  • ドメインモデルレイヤー(シリアル化不可)
  • DTO(同じ理由)
  • メソッドの入力パラメーター
  • コンストラクタパラメータで」

これもいくつかの有効なポイントを上げるようです。

私はそれOptionalが避けられるべきであることを示唆する否定的な意味合いや危険信号を見つけることができませんでした。一般的な考え方は、APIの有用性や使いやすさが向上する場合は、それを使用することだと思います。


11
stackoverflow.com/questions/25693309-ジャクソンはすでにサポートしているようですので、「シリアル化不可」は正当な理由として渡されなくなりました:)
Vlasec

1
Optionalメソッド(より具体的にはコンストラクター)の入力パラメーターで「何も購入しない」理由を回答することをお勧めします。dolszewski.com/java/java-8-optional-use-casesには、わかりやすい説明が含まれています。
ギリ

他のAPIに変換する必要があるオプションの結果をカリー化するには、オプションのパラメーターが必要であることを発見しました。結果はかなりわかりやすいAPIです。stackoverflow.com/a/31923105/105870を
Karl the Pagan

1
リンクが壊れています。答えを更新していただけませんか?
softarn 2017年

2
問題は、開発者の最善の意図にもかかわらず、APIが改善されないことが多いことです。私はOptionalの不適切な使用法の収集例で​​あり、すべて本番用コードから取得したものです。
MiguelMunoz 2018

20

一般に、null値を許容する戻り値にはオプションの型を使用することをお勧めします。ただし、フレームワークについては、古典的なゲッターをオプションの型に置き換えると、ゲッターとセッターのコーディング規約に依存するフレームワーク(Hibernateなど)を操作するときに多くの問題が発生すると私は思います。


14
このアドバイスは、まさに、私がstackoverflow.com/a/26328555/3553087で「熱心な酷使のリスクについて懸念している」という意味でした。
Brian Goetz 2014年

13

OptionalJavaに追加された理由は次のとおりです。

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .findFirst()
    .getOrThrow(() -> new InternalError(...));

これよりもきれいです:

Method matching =
    Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .getFirst();
if (matching == null)
  throw new InternalError("Enclosing method not found");
return matching;

私のポイントは、オプションが Javaに同時に追加された関数型プログラミングをサポートするように作成されたことです。(この例は、Brian Goetzのブログの厚意により提供されています。orElse()このコードはとにかく例外をスローするため、この方法を使用するより良い例ですが、画像を取得します。)

しかし今、人々は非常に異なる理由でオプションを使用しています。彼らは、言語設計の欠陥に対処するためにそれを使用しています。欠点はこれです:どのAPIのパラメーターと戻り値をnullにできるかを指定する方法はありません。それはjavadocsで言及されているかもしれませんが、ほとんどの開発者は自分のコード用のjavadocを書くことすらしていません。したがって、これにより、多くのコードが使用前に常にnull値をチェックします。ただし、呼び出しスタックですでに9回または10回繰り返し検証されているため、nullになることはほとんどありません。

新しいOptionalクラスがその目的をAPIを明確にすることであると想定していた多くの人々が、この欠陥を解決するための本当の渇きがあったと思います。人々が「ゲッターはオプションを返すべきですか?」いいえ、ゲッターが関数型プログラミングで使用されることを期待している場合を除き、おそらく使用すべきではありません。実際、Java APIでOptionalが使用されている場所を見ると、これは主に関数型プログラミングのコアであるStreamクラスにあります。(私は徹底的にチェックしていませんが、Streamクラスが使用される唯一の場所かもしれません。)

少し関数コードでゲッターを使用する予定がある場合は、標準のゲッターとOptionalを返す2番目のゲッターを用意することをお勧めします。

ああ、クラスをシリアル化可能にする必要がある場合は、絶対にOptionalを使用しないでください。

オプションは、a)非常に冗長であり、b)最初からその問題を解決することを意図していないため、APIの欠陥に対する非常に悪いソリューションです。

APIの欠陥に対するはるかに優れた解決策はNullness Checkerです。これは、@ Nullableで注釈を付けることにより、nullにできるパラメーターと戻り値を指定できる注釈プロセッサーです。このようにして、コンパイラーはコードをスキャンして、実際にnullになる可能性のある値がnullが許可されていない値に渡されているかどうかを判断できます。デフォルトでは、注釈が付けられていない限り、nullにすることは許可されていません。これにより、null値を気にする必要がなくなります。パラメータにnull値を渡すと、コンパイラエラーが発生します。nullにできないnullのオブジェクトをテストすると、コンパイラ警告が生成されます。これの効果は、NullPointerExceptionを実行時エラーからコンパイル時エラーに変更することです。

これはすべてを変えます。

ゲッターについては、オプションを使用しないでください。そして、どのメンバーもnullにならないようにクラスを設計してください。Nullness Checkerをプロジェクトに追加して、必要に応じてゲッターとセッターのパラメーター@Nullableを宣言してみてください。私はこれを新しいプロジェクトでのみ行いました。おそらく、nullに関する多くの余分なテストで記述された既存のプロジェクトで多くの警告が生成されるため、改造するのは難しいかもしれません。しかし、それはまた多くのバグをキャッチします。大好きです。そのため、私のコードはよりクリーンで信頼性が高くなっています。

(これに対処する新しい言語もあります。JavaバイトコードにコンパイルされるKotlinを使用すると、オブジェクトを宣言するときにオブジェクトがnullであるかどうかを指定できます。よりクリーンなアプローチです。)

元の投稿への補遺(バージョン2)

多くのことを考えた後、しぶしぶと、1つの条件でOptionalを返すことは許容できるという結論に達しました。つまり、取得した値が実際にはnullである可能性があります。nullを返すことができないゲッターからOptionalを日常的に返すコードをたくさん見ました。私はこれを、コードに複雑さを追加するだけの非常に悪いコーディング慣行と見なしています。しかし、戻り値が実際にはnullである可能性がある場合は、先に進んでそれをOptional内にラップします。

関数型プログラミング用に設計され、関数参照が必要なメソッドは、2つの形式で記述される必要があり、その1つはOptionalを使用することに注意してください。例えば、Optional.map()およびOptional.flatMap()両方のテイク関数参照。最初は通常のゲッターへの参照を受け取り、2番目はOptionalを返すものを受け取ります。したがって、値をnullにすることはできないOptionalを返すことで、誰かに好意を示しているわけではありません。

それでも、Nullness Checkerが使用するアプローチは、NullPointerExceptionを実行時のバグからコンパイル時エラーに変換するため、Nullを処理するための最良の方法だと私は思います。


2
この答えは私には最も適切だと思われます。オプションは、ストリームが追加されたときにJava 8でのみ追加されました。そして、私が見た限りでは、ストリーム関数だけがオプションを返します。
Archit 2018

1
これは最良の答えの1つだと思います。人々はオプションは何であり、どのように機能するかを知っています。しかし、最も混乱する部分は、それをどこでどのように使用するかです。これを読むと、多くの疑問が解消されます。
ParagFlume

3

あなたが最新のシリアライザと理解できる他のフレームワークを使用しているならOptional、私はこれらを見つけましたガイドラインは、書き込み時にうまく機能Entity豆とドメイン層:

  1. シリアライゼーションレイヤー(通常はDB)がtableのnullcolumnのセルの値を許可している場合、ゲッターは、この値がnullであると合理的に予想され、これを処理する必要があることを開発者に示すことができます。DBが値がnullでないことを保証する場合、ゲッターはこれをでラップしないでください。BARFOOFoo.getBar()OptionalOptional
  2. Foo.barあるべきprivateないことOptional。それがOptionalそうであるならば、それがそうである理由は本当にありませんprivate
  3. セッターは、Foo.setBar(String bar)の種類を取る必要barしませ Optionalnull引数を使用してもかまわない場合は、JavaDocコメントでこれを指定します。使用しnullても問題ない場合は、IllegalArgumentException適切なビジネスロジック適切では、IMHOがより適切です。
  4. コンストラクターはOptional引数を必要としません(ポイント3と同様の理由により)。一般に、コンストラクターに引数を含めるだけです。必要がありますシリアライズデータベース内の非nullにします。

上記より効率的にするために、あなたはゲッターを生成し、ためのテンプレートを対応するためにあなたのIDEのテンプレートを編集したい場合がありますtoString()equals(Obj o)(ほとんどのIDE発生器がすでにヌルを扱う)それらのために、直接などまたは使用フィールド。

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