S-JIS[2014-03-29/2018-10-01] 変更履歴

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のメソッド

メソッド(例) ver 処理結果 説明 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
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 {
  case Some(s) => println(s)
  case _ => println("nothing!")
}
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<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)
Optional<String> opt = 〜;
Stream<String> s = opt.stream();
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()) {
  System.out.println(opt.get());
}
値があるときだけその値を表示する。 if (opt.nonEmpty) {
  println(opt.get)
}
 
    opt match {
  case Some(s) => println(s)
  case _ =>
}
 
Java8 opt.ifPresent(s -> System.out.println(s)); opt.foreach(println(_))  
Java8 opt.ifPresent(System.out::println); opt.foreach(println)  
Java8 opt.ifPresent(s -> {
  System.out.print("test ");
  System.out.println(s);
});
opt.foreach { s =>
  print("test ")
  println(s)
}
 
Java8 if (opt.isPresent()) {
  System.out.println(opt.get());
} else {
  System.out.println("nothing!");
}
値があるときはその値を、無いときは別のメッセージを表示する。 if (opt.nonEmpty) {
  println(opt.get)
} else {
  println("nothing!")
}
2017-09-24
Java9 opt.ifPresentOrElse(
  s -> System.out.println(s),
  () -> System.out.println("nothing!")
);
opt match {
  case Some(s) => println(s)
  case _ => println("nothing!")
}
2017-09-24
Java8 System.out.println(opt.orElse("nothing!")); println(opt.getOrElse("nothing!")) 2017-09-24
Java8 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)
 
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

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