箱の中に猫、放射性物質、青酸入りのフラスコがあると想像してください。 物質が非常に少ないため、1時間で1つの原子のみが崩壊します。 1時間以内に壊れると、リーダーは放電され、リレーが作動し、ハンマーが作動してフラスコが壊れ、猫はカラチュンを獲得します。 原子は崩壊するかもしれないし、崩壊しないかもしれないので、猫が生きているかどうかわからないので、生きているか死んでいるかの両方です。 これはシュレーディンガー猫と呼ばれる思考実験です。

Optionalクラスには同様のプロパティがあります-コードを書くとき、開発者はしばしば、プログラムの実行時に必要なオブジェクトが存在するかどうかを知ることができません。そのような場合、nullをチェックする必要があります。 そのようなチェックを怠ると、遅かれ早かれ(通常はより早く)プログラムはNullPointerExceptionでクラッシュします。
同僚! この記事は、他の記事と同様に完全ではなく、修正することができます。 この資料に大幅な改善の可能性がある場合は、コメントでそれを示してください。オプションでオブジェクトを取得する方法は?
既に述べたように、Optionalクラスにはオブジェクトが含まれるか、nullが含まれます。 たとえば、リポジトリから特定のIDを持つユーザーを抽出してみましょう。
User = repository.findById(userId);
おそらく、そのようなIDを持つユーザーはリポジトリにいますが、そうではないかもしれません。 そのようなユーザーがいない場合、スタックトレースでNullPointerExceptionが到着します。 Optionalクラスを用意していない場合は、次のような構造を考案する必要があります。
User user; if (Objects.nonNull(user = repository.findById(userId))) { ( ) }
そうではありません。 この行を扱う方がずっといいです:
Optional<User> user = Optional.of(repository.findById(userId));
要求されたオブジェクトが存在する可能性があるオブジェクトを取得します-または、nullである可能性があります。 しかし、Optionalでは何らかの方法で作業する必要があり、それに含まれる(または含まれない)エンティティが必要です。
オプションのカテゴリは3つのみです。
- Optional.of-Optionalオブジェクトを返します。
- Optional.ofNullable-Optionalオブジェクトを返し、ジェネリックオブジェクトがない場合は空のOptionalオブジェクトを返します。
- Optional.empty-空のOptionalオブジェクトを返します。
ラップされたオブジェクトが存在するかどうかの認識から生じる2つのメソッド-isPresent()およびifPresent();もあります。
.ifPresent()
このメソッドを使用すると、オブジェクトが空でない場合に何らかのアクションを実行できます。
Optional.of(repository.findById(userId)).ifPresent(createLog());
通常、オブジェクトが存在しないときに何らかのアクションを実行する場合(以下で詳細を説明します)、ここでは逆になります。
.isPresent()
このメソッドは、検索対象オブジェクトが存在するかどうかにかかわらず、ブール値の形式で答えを返します。
Boolean present = repository.findById(userId).isPresent();
以下で説明するget()メソッドを使用する場合、このメソッドを使用して、指定されたオブジェクトが存在するかどうかを確認する必要はありません。たとえば:
Optional<User> optionalUser = repository.findById(userId); User user = optionalUser.isPresent() ? optionalUser.get() : new User();
しかし、そのようなデザインは個人的には面倒なようです。オブジェクトを取得するためのより便利な方法については、以下で説明します。
Optionalに含まれるオブジェクトを取得する方法は?
orElse()ファミリーのオブジェクトをさらに取得するための3つの直接メソッドがあります。 翻訳からわかるように、これらのメソッドは、受信したOptionalのオブジェクトが見つからなかった場合に機能します。
- orElse()-デフォルトでオブジェクトを返します。
- orElseGet()-指定されたメソッドを呼び出します。
.orElse()
オブジェクトが空であっても、確実に取得する必要がある場合に適しています。 この場合、コードは次のようになります。
User user = repository.findById(userId).orElse(new User());
この設計は、Userクラスのオブジェクトを返すことが保証されています。 これは、オプションの認識の初期段階で多くの場合に役立ちます。多くの場合、Spring Data JPAの使用に関連しています(findファミリーのクラスのほとんどは正確にOptionalを返します)。
.orElseThrow()
非常に頻繁に、そして再び、Spring Data JPAを使用する場合、たとえばリポジトリ内のエンティティに関しては、そのようなオブジェクトがないことを明示的に宣言する必要があります。 この場合、オブジェクトを取得するか、そうでない場合は例外をスローできます。
User user = repository.findById(userId).orElseThrow(() -> new NoEntityException(userId));
エンティティが検出されず、オブジェクトがnullの場合、NoEntityExceptionがスローされます(私の場合、カスタムエンティティ)。 私の場合、「User {userID}行が見つかりません。 リクエストの詳細を確認してください。」
.orElseGet()
オブジェクトが見つからない場合、「オプション」は「オプションB」用のスペースを残します。次のような別のメソッドを実行できます。
User user = repository.findById(userId).orElseGet(() -> findInAnotherPlace(userId));
オブジェクトが見つからなかった場合は、他の場所を検索することをお勧めします。
orElseThrow()などのこのメソッドは、Supplierを使用します。 また、このメソッドを使用して、再び.orElse()のようにデフォルトオブジェクトを呼び出すことができます。
User user = repository.findById(userId).orElseGet(() -> new User());
オブジェクトを取得するためのメソッドに加えて、ストリーム()から道徳的に継承されたオブジェクトを変換するための豊富なツールボックスがあります。
受信したオブジェクトを操作します。
上で書いたように、Optionalには、結果のオブジェクトを変換するための優れたツールキットがあります。
- map()-オブジェクトを別のオブジェクトに変換します。
- filter()-含まれるオブジェクトを述語でフィルタリングします。
- flatmap()-セットをストリームの形式で返します。
.get()
get()メソッドは、オプションでラップされたオブジェクトを返します。 例:
User user = repository.findById(userId).get();
Userオブジェクトが取得され、オプションでラップされます。 このような構造は、ヌルチェックを通過し、必要なオブジェクトを取得できるため、またはNPEを取得できるため、Optional自体の使用を無意味にするため、非常に危険です。 この設計は、.isPresent()でラップする必要があります。
.map()
このメソッドは、stream()に対して同様のメソッドを完全に繰り返しますが、Optionalにnull以外のオブジェクトがある場合にのみ機能します。
String name = repository.findById(userId).map(user -> user.getName()).orElseThrow(() -> new Exception());
この例では、オプションでパックされたUserクラスのフィールドの1つを取得しました。
.filter()
このメソッドもストリーム()から借用され、条件によって要素をフィルタリングします。
List<User> users = repository.findAll().filter(user -> user.age >= 18).orElseThrow(() -> new Exception());
.flatMap()
このメソッドは、ストリームのメソッドとまったく同じことを行いますが、唯一の違いは、値がnullでない場合にのみ機能することです。
おわりに
Optionalクラスを巧みに使用すると、NullPoinerExceptionでクラッシュするアプリケーションの機能が大幅に低下し、無数のnullチェックを行う場合よりもわかりやすくコンパクトになります。 また、人気のあるフレームワークを使用する場合、同じSpringがメソッドのテールとたてがみの両方でこのクラスを駆動するため、このクラスを詳細に検討する必要があります。 ただし、オプションはJava 8の買収です。つまり、2018年にJava 8を知ることは必須です。