JavaのOptionalクラスについて。
|
Optionalは、値を1つラップする(包む)クラス。JDK1.8以降。(ScalaのOptionクラスに相当する)
import java.util.Optional;
Optionalはただ単に値を保持しているだけだが、Optionalの各メソッドは、保持している値がnullか否かによって挙動が変わる。(基本的に、null以外のときだけ処理が行われる)
Optionalのインスタンスは、内包している値がnullの場合はemptyというオブジェクトになり、値が有る場合は普通に値を保持するインスタンスとなる。
Optionalを受け取った側は、そのOptionalがemptyかどうかを判定して処理を行う。
空かどうかの判定を自分でしないといけないなら、nullかどうかの判定を自分で行うのと大差ないが、
判定を自動的に行ってくれるメソッドが存在しているところがOptionalの利点。
Javaには、“ある値を返すが、特定の場合だけ値を返せない”というメソッドがよくある。
例えばjava.util.Map#get()は、キーがあればそれに紐付く値を返すが、キーが無い場合はnullを返す。
例えばString#indexOf()は、見つけた文字の位置を返すが、見つからなかった場合は-1を返す。
そういったメソッドの戻り値を表現するのがOptionalの役割。
例えばMap#get()がOptionalを返したとしたら、キーがあればOptional#get()で値を取得できるし、キーが無い場合はOptionalはemptyになる。
例えばString#indexOf()がOptional<Integer>(あるいはOptionalInt)を返したとしたら、文字を見つけた場合はOptional#get()(あるいはOptionalInt#getAsInt())は見つけた位置を返すし、見つからなかった場合はOptional(OptionalInt)はemptyになる。
今後は、“返り値が必ずあるとは限らない”メソッドでは、Optionalを返すようになっていくと思われる。
メソッド(例) | ver | 処理結果 | 説明 | Scalaの例 | |
---|---|---|---|---|---|
値が有るとき | 値がnullのとき | ||||
Optional.ofNullable(値) |
Optional | Optional.empty | Optionalオブジェクトを生成する。 値がnullの場合はOptional.emptyが返る。 |
Option(値) |
|
Optional.of(値) |
Optional | NullPointerExceptionが発生 | Optionalオブジェクトを生成する。 値がnullの場合はNullPointerExceptionが発生する。 (ScalaのSomeの場合、nullを渡したらnullを保持したSomeが返る) |
Some(値) |
|
Optional.empty() |
- | Optional.empty | Optional.emptyオブジェクトを返す。 |
None |
|
opt.isPresent() |
true | false | null以外の値を保持しているかどうかを返す。 |
opt.isDefined |
|
opt.isEmpty() |
11 | false | true | nullかどうかを返す。[2018-10-01] |
opt.isEmpty |
opt.ifPresent(s
-> System.out.println(s)); |
入っている値 が表示される |
(何もなし) | ifPresent()には処理を行う関数(Consumer)を渡す。 保持している値がnull以外の場合だけ処理が行われる。 |
opt.foreach(println(_)) |
|
opt.ifPresent(System.out::println); |
opt.foreach(println) |
||||
opt.ifPresentOrElse(s ->
System.out.println(s), () -> System.out.println("nothing!")); |
9 | 入っている値 が表示される |
「nothing!」が表示される | 保持している値がnull以外のときは第1引数、nullのときは第2引数の処理を実行する。[2017-09-24] |
opt match { |
opt.get() |
入っている値 | NoSuchElementExceptionが発生 | 保持している値を返す。 保持している値がnullの場合はNoSuchElementExceptionが発生する。 Java10でorElseThrow()が追加になった。[2018-06-02] get()に@Deprecatedアノテーションは付けられていないが、orElseThrowの方が(名前 から動作が分かりやすいので)推奨っぽい。 |
opt.get |
|
opt.or(() -> Optional.of(値)) |
9 | 自分自身 | 指定されたOptional(値) | 保持している値がnull以外の場合は自分自身、nullなら指定されたOptionalを返す。[2017-09-24] |
opt.orElse(Option(値)) |
opt.orElse(デフォルト値) |
入っている値 | 指定されたデフォルト値 | 保持している値を返す。 保持している値がnullの場合は指定されたデフォルト値を返す。 |
||
opt.orElse(null) |
入っている値 | null |
opt.orNull |
||
opt.orElseGet(()
-> デフォルト値) |
入っている値 | 指定されたデフォルト値 | 保持している値を返す。 orElseGet()にはデフォルト値を返す関数(Supplier)を渡す。 保持している値がnullの場合は、指定された関数を呼び出してその値を返す。 (デフォルト値の生成は、nullのときしか行われないことになる。 いわゆる「遅延評価」である) |
opt.getOrElse(デフォルト値) |
|
opt.orElseThrow() |
10 | 入っている値 | NoSuchElementExceptionが発生 | 保持している値を返す。[2018-06-02] 動作はget()と全く同じ。 |
|
opt.orElseThrow(()
-> new RuntimeException()) |
入っている値 | 指定された例外が発生 | 保持している値を返す。 orElseThrow()には例外を返す関数(Supplier)を渡す。 保持している値がnullの場合は、指定された関数が返す例外をスローする。 (例外インスタンスの生成は、nullのときしか行われない) |
||
opt.orElseThrow(RuntimeException::new) |
|||||
Optional<String> opt = 〜; |
Optional(変換した値) | Optional.empty | map()には変換を行う関数(Function)を渡す。 値があるときはその値を使って変換した別のOptionalを返す。 (変換関数がnullを返すとOptional.emptyになる) 保持している値がnullの場合はOptional.emptyを返す。 |
val o2 = opt.map(_.length) |
|
Optional<String> opt = 〜; |
変換したOptional | Optional.empty | flatMap()には、Optionalを返すような変換関数(Function)を渡す。 |
val o3 = opt.flatMap { s => |
|
Optional<String> opt = 〜; |
条件を満たした場合は自分自身 満たさない場合はOptional.empty |
Optional.empty | filter()には条件判定を行う関数(Predicate)を渡す。 条件を満たすものを抽出する。 (保持している値がnullだったら、常にOptional.emptyを返す。 値がある場合は、条件を満たした場合は自分自身を返すが、 条件を満たさない場合はOptional.emptyを返す) |
val o4 = opt.filter(_.length >= 2) |
|
Optional<String> opt = 〜; |
9 | Stream.of(入っている値) | Stream.empty() | Streamに変換する。[2017-09-24] 保持している値がnull以外だったらその値を持つStream、 nullだった場合は空のStream。 Stream#flatMap()でとても便利。 |
opt.toStream |
ちなみに、Scalaの場合はOptionクラスは抽象クラスであり、具象クラスとしてSomeがあり、空を表すNoneというオブジェクトがある。
JavaのOptionalはfinalクラスであり、そのクラスだけで完結している。
Optional自体はオブジェクトを対象とするが、プリミティブのint・long・doubleを扱うクラスは別途用意されている。[2014-03-30]
Optional | OptionalInt | OptionalLong | OptionalDouble | 備考 | 更新日 |
---|---|---|---|---|---|
Optional<T> Optional.ofNullable(T) |
|||||
Optional<T> Optional.of(T) |
OptionalInt OptionalInt.of(int) |
OptionalLong OptionalLong.of(long) |
OptionalDouble OptionalDouble.of(double) |
||
Optional<T> Optional.empty() |
OptionalInt OptionalInt.empty() |
OptionalLong OptionalLong.empty() |
OptionalDouble OptionalDouble.empty() |
||
boolean opt.isPresent() |
boolean opt.isPresent() |
boolean opt.isPresent() |
boolean opt.isPresent() |
||
boolean opt.isEmpty() |
boolean opt.isEmpty() |
boolean opt.isEmpty() |
boolean opt.isEmpty() |
Java11 | 2018-10-01 |
void opt.ifPresent((T)->void) |
void opt.ifPresent((int)->void) |
void opt.ifPresent((long)->void) |
void opt.ifPresent((double)->void) |
Consumer | |
void opt.ifPresentOrElse((T)->void,
()->void) |
void opt.ifPresentOrElse((int)->void,
()->void) |
void opt.ifPresentOrElse((long)->void,
()->void) |
void opt.ifPresentOrElse((double)->void,
()->void) |
Java9 | 2017-09-24 |
T opt.get() |
int opt.getAsInt() |
long opt.getAsLong() |
double opt.getAsDouble() |
||
Optional<T> opt.orElse(()->Optional<T>) |
Java9 | 2017-09-24 | |||
T opt.orElse(T) |
int opt.orElse(int) |
long opt.orElse(long) |
double opt.orElse(double) |
||
T opt.orElseGet(()->T) |
int opt.orElseGet(()->int) |
long opt.orElseGet(()->long) |
double opt.orElseGet(()->double) |
Supplier | |
T opt.orElseThrow() |
int opt.orElseThrow() |
long opt.orElseThrow() |
double opt.orElseThrow() |
Java10 | 2018-06-02 |
T opt.orElseThrow(()->X) |
int opt.orElseThrow(()->X) |
long opt.orElseThrow(()->X) |
double opt.orElseThrow(()->X) |
Supplier | |
U opt.map((T)->U) |
Function | ||||
Optional<U> opt.flatMap((T)->Optional<U>) |
Function | ||||
Optional<T> opt.filter((T)->boolean) |
Predicate | ||||
Stream<T> opt.stream() |
IntStream opt.stream() |
LongStream opt.stream() |
DoubleStream opt.stream() |
Java9 | 2017-09-24 |
OptionalとOptionalInt等の相互変換は出来ないようだ。(継承関係も無いし、変換に使えそうなメソッドも無い)
ちなみに、Scalaの場合はプリミティブ型という概念は無く、数値型(IntやLong・Double等)も普通のクラス扱いなので、全てOptionクラスを使用する。
ver | 例 | 説明 | Scalaの例 | 更新日 |
---|---|---|---|---|
Java8 |
if (opt.isPresent()) { |
値があるときだけその値を表示する。 |
if (opt.nonEmpty) { |
|
opt match { |
||||
Java8 |
opt.ifPresent(s
-> System.out.println(s)); |
opt.foreach(println(_)) |
||
Java8 |
opt.ifPresent(System.out::println); |
opt.foreach(println) |
||
Java8 |
opt.ifPresent(s
-> { |
opt.foreach { s => |
||
Java8 |
if (opt.isPresent()) { |
値があるときはその値を、無いときは別のメッセージを表示する。 |
if (opt.nonEmpty) { |
2017-09-24 |
Java9 |
opt.ifPresentOrElse( |
opt match { |
2017-09-24 | |
Java8 |
System.out.println(opt.orElse("nothing!")); |
println(opt.getOrElse("nothing!")) |
2017-09-24 | |
Java8 |
Optional<String> opt = 〜; |
文字が見つかった場合はその位置、 文字が見つからなかった場合はOptional.emptyを返す。 |
val o3 = opt. |
|
Java8 |
Stream<String> s = opt.isPresent()
? Stream.of(opt.get()) : Stream.empty(); |
Streamへの変換。 |
val s = if (opt.nonEmpty) Stream(opt.get) else Stream.empty |
2017-09-24 |
Java9 |
Stream<String> s = Stream.ofNullable(opt.orElse(null)); |
2017-09-24 | ||
Java9 |
Stream<String> s = opt.stream(); |
val s = opt.toStream |
2017-09-24 |