Java 8 | Optional

6386 ワード

レンガを運ぶ過程で、きっとNPE(NullPointerException)に出会ったことがあると思います.まだ初期化されていない変数やnullに初期化されていない変数を使用しようとすると、NPEが表示されます.Java 8はOptionalを追加してこの問題を解決しようとした.次に、このクラスについて理解してみましょう.
空(Null)はいったいどんなタイプですか.
Javaでは、リファレンスを使用してオブジェクトインスタンスにアクセスします.このリファレンスがそのオブジェクトインスタンスを指すことを知らない場合は、通常nullに設定します.
普段レンガを運ぶ過程でnullの使用は一般的で、私たちはそれを考慮することはめったにありません.特に指定されていない場合、オブジェクトのインスタンス変数は通常nullに自動的に初期化され、この参照にどの値を割り当てるか分からない場合、nullは通常割り当てられます.
では、空(Null)はいったいどんなタイプなのでしょうか.
参考までに、Javaでは空(Null)も実はタイプですが、それは特殊で、nullという字面量をこのタイプに割り当てるしかありません.また、このタイプは他のJavaタイプとは異なり、nullはエラーなしに他のJavaタイプに値を割り当てることができます.
nullを返すのに何か問題がありますか?
開発の過程では、サードパーティが提供するAPIインタフェースをよく使用しますが、これらのAPIインタフェースの戻り値はnullである可能性があり、戻り結果の説明はAPIドキュメントにすでに説明されている可能性がありますが、私たちが開発したときは、様々な理由でドキュメント(ドキュメントを読んだこともない)をよく読んでおらず、戻り値がnullであることを処理することを忘れてしまいました.これでNPEの問題が発生する可能性があります.また、このような現象は普段の開発過程でよく発生している.
では、どのようにしてこの問題を効果的に解決しますか?比較的良い方法は、戻り値を初期化することですが、初期化の値はnullではありません(例として、メソッドの戻り値がリストであれば、Collections.emptyList()を返すことができます).これでNPEは現れません.この理屈ではありませんか.
うん、理想は美しくて、現実は骨っぽいです.実際にはnullではない初期化値が見つからない可能性がありますが、どうすればいいのでしょうか.するとOptionalが現れた.
Java 8のOptionalはどのようにNPEを解決しますか?Optionalは、空で参照される非空の値です.Optionalには、空でない参照が含まれているか、何も含まれていない可能性があります.Optionalにnullが含まれているとは言わないでください.
Optional canBeEmpty1 = Optional.of(5);
canBeEmpty1.isPresent();                    // returns true
canBeEmpty1.get();                          // returns 5

Optional canBeEmpty2 = Optional.empty();
canBeEmpty2.isPresent();                    // returns false

Optionalを単一の値容器とすることができます.中には値が入っているかもしれません.何も入っていないかもしれません.
なお、nullの代わりにOptionalクラスが追加されたのではなく、Optionalクラスが追加されたのは、より容易に理解できるAPIインタフェースを設計するためである.たとえば
public User getUserFromDB(long id);

このインタフェースを呼び出すと、Userというリファレンスをそのまま使用して、リファレンスが空である可能性があると判断するのを忘れてしまう可能性があります.もしこのインタフェースが
public Optional getUserFromDB(long id);

これがインタフェースであることを見ると、このインタフェースが返す結果が空になる可能性があることに気づき、この値を判断してから使用します.一目でわかったのではないでしょうか.Optionalを使用した例をいくつか挙げます
a)Optionalオブジェクトの作成
Optionalオブジェクトを作成するには3つの方法があります.
i)Optionalを使用する.Empty()値のないOptionalコンテナを作成します.
Optional possible = Optional.empty();

ii)Optionalを使用する.of()値が入ったOptionalコンテナを作成します.もしあなたがOptionalにnullを伝えたら.of()メソッドは、NullPointerExceptionを放出します.
Optional possible = Optional.of(5);

iii)Optionalを使用する.ofNullable()は、値がない可能性のあるOptionalコンテナを作成します.
Optional possible = Optional.ofNullable(null);
//or
Optional possible = Optional.ofNullable(5);

b)Optional容器有値処理
Optionalオブジェクトが取得された場合は、まずOptionalコンテナに値があるかどうかを確認する必要があります.
Optional possible = Optional.of(5);
possible.ifPresent(System.out::println);

もちろん、上のコードは次のように伝統的に書くこともできます.
if(possible.isPresent()){
    System.out.println(possible.get());
}

しかし、これはお勧めの書き方ではありません.そうしないと、Java 8のlambda式は何の役に立ちますか.
c)Optional容器無値処理
戻り値が存在しない場合、通常はデフォルト値を返します.一般的には三元演算子を使って処理しますが、Optionalを使ってこのようにすることができます.
//Assume this value has returned from a method
Optional companyOptional = Optional.empty();
 
//Now check optional; if value is present then return it,
//else create a new Company object and retur it
Company company = companyOptional.orElse(new Company());
 
//OR you can throw an exception as well
Company company = companyOptional.orElseThrow(IllegalStateException::new);

d)filter法によるろ過
通常、私たちは結果を返すには、この結果をいくつかフィルタリングし、私たちの要求に合った結果をフィルタリングする必要があります.
Optional companyOptional = Optional.empty();
companyOptional.filter(department -> "Finance".equals(department.getName())
                    .ifPresent(() -> System.out.println("Finance is present"));

filterメソッドはpredicateパラメータを受け入れます.Optionalに値があり、predicateを満たすと、filterメソッドはこの値を返します.そうしないと、値のないOptionalが返されます.
わあ、今はコードにいろいろなnull値を書いて論理を判断する必要はありません.もっと簡潔に見えますか.
Optional内部はどのように処理されていますか?Optional.java、Optionalが持つ値定義を見てみましょう
/**
 * If non-null, the value; if null, indicates no value is present
 */
private final T value;

値が空の場合、静的変数表現が使用されます.
/**
 * Common instance for {@code empty()}.
 */
private static final Optional> EMPTY = new Optional<>();
Optionalクラスのコンストラクション関数はすべてプライベートであり、上記の3つのOptionalインスタンスの作成しか使用できないことを確認します.
値のあるOptionalインスタンスを作成すると、入力されたvalueが判断されます.
this.value = Objects.requireNonNull(value);

Optionalから値を取得すると、valueがnullの場合、NoSuchElementException例外が放出されます.
public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

同様に、Optionalクラスの他の方法は、このvalueをめぐって行われる.
Opti 0 nalが解決しようとしたOptionalクラスはJavaにおけるNPE問題の解決を試みる.Optionalクラスでより分かりやすいAPIを設計し、Optionalクラスが最初から設計されていれば、現在のクラスライブラリ、アプリケーションでnull値の処理がより合理的になると信じています.Optional類を用いることで,今回は開発者に空の異常状況を考えるように強要した.APIを記述する場合、戻り値がOptionalクラスであれば、APIを記述する開発者はnullを直接返すことができないため、コンパイルも通過しないと考えざるを得ない.
Opti 0 nalでは解決できない
前述したように、Opti0nalはすべてのnullの場合に代わるものではありません.関数のパラメータにnullがある場合、Opti0nalの使用は推奨されません.インタフェースを呼び出すと、Opti0nalオブジェクトにカプセル化しなければなりません.ちょっと面倒ではありませんか.Opti0nal類をより優雅に使用するためには、以下の場合は使用しないほうがいいです.
  • データモデル層(Opti 0 nalは直列化できない)
  • DTO(Opti 0 nalは直列化できない)
  • メソッドのパラメータにおける
  • 構造関数における
  • Opti 0 nalはどこで使いますか?Opti0nalは、空に戻る可能性のあるメソッドの戻り値タイプとして使用されるべきである.
    次の引用時のOpenJDKの部分
    The JSR-335 EG felt fairly strongly that Optional should not be on any more than needed to support the optional-return idiom only. Someone suggested maybe even renaming it to "OptionalReturn".
    この言葉はOpti0nalが空に戻る可能性のある方法としての戻り値のタイプを表していると思います.
    まとめ
    本文は主にjava.util.Optionalの使用を紹介した.Opti0nalクラスの目的はnullに取って代わることではなく、より理解されたAPIインタフェースの設計を支援し、方法によって値を返し、開発者にnullの異常が発生する可能性があることを処理するように注意することである.