S-JIS[2014-03-29/2014-03-30] 変更履歴

Java Optional

JavaのOptionalクラスについて。


概要

Optionalは、値を1つラップする(包む)クラス。JDK1.8以降。(ScalaOptionクラスに相当する)

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を返すようになっていくと思われる。


Optionalのメソッド

メソッド(例) 処理結果 説明 Scalaの例
値が有るとき 値がnullのとき
Optional.ofNullable(値) Optional Optional.empty Optionalオブジェクトを生成する。
値がnullの場合はOptional.emptyが返る。
Option(値)
Option.apply(値)
Optional.of(値) Optional NullPointerExceptionが発生 Optionalオブジェクトを生成する。
値がnullの場合はNullPointerExceptionが発生する。
(ScalaのSomeの場合、nullを渡したらnullを保持したSomeが返る)
Some(値)
Some.apply(値)
Optional.empty() - Optional.empty Optional.emptyオブジェクトを返す。 None
opt.isPresent() true false null以外の値を保持しているかどうかを返す。 opt.isDefined
opt.nonEmpty
  false true   opt.isEmpty
opt.ifPresent(s -> System.out.println(s)); 入っている値
が表示される
(何もなし) ifPresent()には処理を行う関数(Consumer)を渡す。
保持している値がnull以外の場合だけ処理が行われる。
opt.foreach(println(_))
opt.ifPresent(System.out::println); opt.foreach(println)
opt.get() 入っている値 NoSuchElementExceptionが発生 保持している値を返す。
保持している値がnullの場合はNoSuchElementExceptionが発生する。
opt.get
opt.orElse(デフォルト値) 入っている値 指定されたデフォルト値 保持している値を返す。
保持している値がnullの場合は指定されたデフォルト値を返す。
 
opt.orElse(null) 入っている値 null opt.orNull
opt.orElseGet(() -> デフォルト値) 入っている値 指定されたデフォルト値 保持している値を返す。
orElseGet()にはデフォルト値を返す関数(Supplier)を渡す。
保持している値がnullの場合は、指定された関数を呼び出してその値を返す。
(デフォルト値の生成は、nullのときしか行われないことになる。
 いわゆる「遅延評価」である)
opt.getOrElse(デフォルト値)
opt.orElseThrow(() -> new RuntimeException()) 入っている値 指定された例外が発生 保持している値を返す。
orElseThrow()には例外を返す関数(Supplier)を渡す。
保持している値がnullの場合は、指定された関数が返す例外をスローする。
(例外インスタンスの生成は、nullのときしか行われない)
 
opt.orElseThrow(RuntimeException::new)
  自分自身 指定されたOption(値)   opt.orElse(Option(値))
Optional<String> opt = 〜;
Optional<Integer> o2 = opt.map(s -> s.length());
Optional(変換した値) Optional.empty map()には変換を行う関数(Function)を渡す。
値があるときはその値を使って変換した別のOptionalを返す。
(変換関数がnullを返すとOptional.emptyになる)
保持している値がnullの場合はOptional.emptyを返す。
val o2 = opt.map(_.length)
Optional<String> opt = 〜;
Optional<Integer> o3 = opt.flatMap(s -> {
  int n = s.indexOf('a');
  return (n >= 0) ? Optional.of(n) : Optional.empty();
});
変換したOptional Optional.empty flatMap()には、Optionalを返すような変換関数(Function)を渡す。 val o3 = opt.flatMap { s =>
  val n = s.indexOf('a')
  if (n >= 0) Option(n) else None
}
Optional<String> opt = 〜;
Optional<String> o4 = opt.filter(s -> s.length() >= 2);
条件を満たした場合は自分自身
満たさない場合はOptional.empty
Optional.empty filter()には条件判定を行う関数(Predicate)を渡す。
条件を満たすものを抽出する。
(保持している値がnullだったら、常にOptional.emptyを返す。
 値がある場合は、条件を満たした場合は自分自身を返すが、
 条件を満たさない場合はOptional.emptyを返す)
val o4 = opt.filter(_.length >= 2)

ちなみに、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()  
void opt.ifPresent((T)->void) void opt.ifPresent((int)->void) void opt.ifPresent((long)->void) void opt.ifPresent((double)->void) Consumer
T opt.get() int opt.getAsInt() long opt.getAsLong() double opt.getAsDouble()  
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(()->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

OptionalとOptionalInt等の相互変換は出来ないようだ。(継承関係も無いし、変換に使えそうなメソッドも無い)

ちなみに、Scalaの場合はプリミティブ型という概念は無く、数値型(IntやLong・Double等)も普通のクラス扱いなので、全てOptionクラスを使用する。


説明 Scalaの例
if (opt.isPresent()) {
  System.out.println(opt.get());
}
値があるときだけその値を表示する。 if (opt.nonEmpty) {
  println(opt.get)
}
  opt match {
  case Some(s) => println(s)
  case _ =>
}
opt.ifPresent(s -> System.out.println(s)); opt.foreach(println(_))
opt.ifPresent(System.out::println); opt.foreach(println)
opt.ifPresent(s -> {
  System.out.print("test ");
  System.out.println(s);
});
opt.foreach { s =>
  print("test ")
  println(s)
}
Optional<String> opt = 〜;
Optional<Integer> o3 = opt.
  map(s -> s.indexOf('a')).
  filter(n -> n >= 0);
文字が見つかった場合はその位置、
文字が見つからなかった場合はOptional.emptyを返す。
val o3 = opt.
  map(_.indexOf('a')).
  filter(_ >= 0)

Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま