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>>
何かを追加します - 特にnull
の値などを削除するためにfilter()
を使うことができますが、コレクションの中でOptional
を使う方法はありますか?
見逃したケースはありますか?
Optional
の主なポイントは、戻り値が存在しないことを示す値を返す関数の手段を提供することです。 この説明 を参照してください。これにより、呼び出し元は流なメソッド呼び出しのチェーンを継続できます。
これは、OPの質問のユースケース#1に最も近いものです。ただし、値の欠如は、nullよりも正確な定式化です。なぜなら、IntStream.findFirst
のようなものは決してnullを返すことができないからです。
ユースケース#2の場合、オプションの引数をメソッドに渡すと、これを機能させることができますが、かなり不器用です。文字列の後にオプションの2番目の文字列が続くメソッドがあるとします。 Optional
を2番目の引数として受け入れると、次のようなコードになります。
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
を実際にフィールドまたはコレクションに格納したいという人が思いつくケースを誰かが思い付くと思いますが、一般的にはこれを避けるのが最善です。
私はゲームに遅れています、しかしそれが価値があるもののために、私は私の2セントを加えたいです。彼らは Optional
の設計目標 に反していますが、これは Stuart Marksの答え でよくまとめられていますが、その妥当性を私は確信しています(明らかに)。
私は全体を書きました Optional
の使い方についてのブログ記事 しかし、それは基本的にこれに帰着します:
Optional
ではなくnull
を使用するようにします。最初の2つの例外は、Optional
で参照をラップおよびラップ解除することで認識されるオーバーヘッドを減らすことができます。それらは、nullが合法的にあるインスタンスから別のインスタンスへ境界を通過できないように選択されます。
これはコレクションでOptional
sを許可することはほとんどなく、null
sとほぼ同じくらい悪いことに注意してください。しないでください。 ;)
これを行うと、コードベースにnull
が含まれることが少なくなりますが、根絶はされません。しかしそれは重要なことでさえありません。他にも重要な利点があります。
Optional
を使用すると、変数がオプションであることを明確に表します。あなたのコードを読んだり、あなたのAPIを消費したりする人は、何もないかもしれないし、値にアクセスする前にチェックが必要であるという事実で頭を殴られます。
Optional
がないと、null
オカレンスの意味が明確になりません。状態の正しい表現( Map.get
を参照)または初期化の欠落や失敗などの実装エラーが考えられます。
これはOptional
を永続的に使用することで劇的に変化します。ここで、すでにnull
が出現しているということは、バグが存在することを意味します。 (値が欠落していることが許されている場合は、Optional
が使用されていたはずです。)これは、このnull
の意味の質問にすでに答えられているので、NULLポインター例外のデバッグをはるかに容易にします。
これでnull
はできなくなりました。これはどこでも実施できます。注釈、アサーション、プレーンチェックのいずれを使用しても、この引数またはその戻り型がnullになる可能性があるかどうかを考える必要はありません。できません。
もちろん、銀の弾丸はありません...
値(特にプリミティブ)を余分なインスタンスにラップすると、パフォーマンスが低下する可能性があります。タイトなループでは、これは目立つようになるかさらにはさらに悪くなるかもしれません。
コンパイラは、寿命の短いOptional
sについての追加の参照を回避できる可能性があることに注意してください。 Java 10では、 value types はさらに不利益を減らすか、または取り除くかもしれません。
Optional
は直列化できません しかし 回避策 はそれほど複雑ではありません。
Javaではジェネリック型が不変であるため、実際の値型がジェネリック型の引数にプッシュされると、特定の操作が面倒になります。例を示します ここで(「パラメトリック多態性」を参照) 。
個人的には、@NotNull
と@Nullable
のチェックはコンパイル時に実行されるため(一部のランタイムチェックを行うことができます)、 IntelliJのコード検査ツール を使用することをお勧めします。 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で動作し、値をラップしたりアンラップしたりする必要はありません。 (またはラッパーオブジェクトを作成する)
私はGuava Optionalと彼らのWikiページがそれを非常にうまくいっていると思います。
Nullに名前を付けることによる可読性の向上に加えて、Optionalの最大の利点は、そのばかな証拠です。あなたは、あなたがプログラムをまったくコンパイルしたいのなら、欠けているケースについて積極的に考えるようにあなたに強制します。 Nullを使用すると、単に物事を忘れることが厄介になりやすくなります。FindBugsが役に立ちますが、それでも問題が解決されるとは限りません。
これは、存在しているかどうかにかかわらず値を返す場合に特に重要です。あなた(および他の人)は、other.methodを実装するときにaがnullである可能性があることを忘れている可能性があるよりも、other.method(a、b)がnull値を返す可能性があることを忘れがちです。 Optionalを返すと、呼び出し側がそのケースを忘れることは不可能になります。コードをコンパイルするには、オブジェクトをラップ解除する必要があるからです。 情報源:---(Guava Wiki - nullを使用し、回避する - 要点は何ですか? )
Optional
name__はオーバーヘッドを増やしますが、その明らかな利点はexplicitにすることでオブジェクトが存在しない可能性があり、プログラマが状況を処理できるようになることです。誰かが最愛の!= null
チェックを忘れるのを防ぎます。
2 を例にとると、もっと明示的に書くコードだと思います。
if(soundcard.isPresent()){
System.out.println(soundcard.get());
}
より
if(soundcard != null){
System.out.println(soundcard);
}
私にとってはOptional
name__はサウンドカードが存在しないという事実をよりよく捉えています。
私の2¢あなたのポイントについて:
public Optional<Foo> findFoo(String id);
- これについてはよくわかりません。たぶん私はemptyであるかもしれないResult<Foo>
を返すか、またはFoo
name__を含むでしょう。これは似たような概念ですが、実際はOptional
name__ではありません。public Foo doSomething(String id, Optional<Bar> barOptional);
- Peter Lawrey's answer - 同様に@Nullableとfindbugsチェックをお勧めします この議論 も参照してください。Optional<Index> getIndex()
を使用します。一般的に、私はnull
name__sを回避することを最小限に抑えるようにします。 (一度焼けて…)適切な抽象化を見つけて、特定の戻り値が実際に何を表しているのかを他のプログラマに知らせることは価値があると思います。
Optionalの目的は、コードベース内のすべてのnull参照を置き換えるのではなく、メソッドのシグニチャを読むだけで、ユーザがオプションの値を期待できるかどうかを判断できる、より優れたAPIの設計を支援することです。さらに、Optionalは、値の欠如に対処するためにOptionalの積極的な展開を強制します。その結果、意図しないNULLポインタ例外からコードを保護できます。
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");
それぞれのgetterメソッドの戻り値としてOptionalを使用します。
この機能についてはすでにいくつかのリソースがあります。
これは興味深い使用法です(私は信じています)...テスト。
私は自分のプロジェクトの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;
}
}
これは、ラベルを確認したい場合にのみアサートが実際にトリガーされることを意味します。
Optional
はOptionalの型Tがint
、long
、char
などのようなプリミティブ型の場合にのみ役に立ちます。 "real"クラスの場合は、null
値を使用できるので意味がありません。
私はそれがここから(または他の同様の言語概念から)取られたと思います。
C#では、このNullable<T>
は値型をラップするためにずっと前に導入されました。
Optional
クラスを使用すると、null
を使用せずに、より良い代替手段を提供できます。
これにより、開発者は、存在しないNullPointerException
を回避するためにプレゼンスをチェックするようになります。
存在しない可能性がある値をどこで見ればよいかを確認することができるため、APIはより文書化されやすくなります。
Optional
は、オブジェクトをさらに操作するための便利なAPIを提供します。 isPresent()
; get()
; orElse()
; orElseGet()
; orElseThrow()
; map()
; filter()
; flatmap()
。
さらに、多くのフレームワークはこのデータ型を積極的に使用してAPIから返します。
Optionalがnull値を返す可能性があるメソッドの一般的な代替品であるとは思いません。
基本的な考え方は次のとおりです。値が存在しなくても、将来その値が使用可能になるとは限りません。それはfindById(-1)とfindById(67)の違いです。
呼び出し側にとってのオプションの主な情報は、彼が与えられた値を当てにしないかもしれないということですが、それはいつか利用可能かもしれません。多分それは再び消えて後でもう一度戻ってくるでしょう。それはオン/オフスイッチのようなものです。あなたはライトをオンまたはオフに切り替えるための「オプション」があります。あなたがオンにするためのライトを持っていない場合しかし、あなたは選択肢がありません。
そのため、以前nullが返された可能性がある場所にOptionalsを導入するのは面倒です。私はまだnullを使いますが、それは木の根、遅延初期化、明示的なfind-methodsのような制限された領域でのみです。
Optional
変更不可能なイテレータデザインパターンのインスタンスと同じ意味を持ちます :
isPresent()
によって与えられるように)get()
を使用して)参照解除することができます。next()
メソッドはありません)。したがって、以前はJavaの使用を検討していた可能性があるコンテキストでOptional
を返すか渡すことを検討してください Iterator
。