Java8 Optional
Java8 Optionalについてのメモです。
Java8で追加されたOptionalについて書いてなかったのでメモです。
Optional
Java8でOptionalが追加されました。
OptionalはあるT型のオブジェクトについてのラッパーで、
T型の値が存在する状態、もしくは何も存在しない状態をラップしています。
Optionalではnullチェックや、nullだった場合の処理などのメソッドがあるため、
正しく使用することで、nullチェックの煩雑さを軽減できたり、nullに対しての安全度が増します。
of
of()で引数で指定したクラスのOptionalを返します。ただし、引数で指定する値はnull以外である必要があります。
@Test public void of() { Zoo zoo = new Zoo("zoo"); Optional<Zoo> ofZoo = Optional.of(zoo); assertThat(ofZoo.get().getClass()).isEqualTo(Zoo.class); }
of(nullの場合)
of()の引数がnullの場合、NullPointerExceptionが発生します。
@Test(expected = NullPointerException.class) public void ofButNull() { Zoo zoo = null; Optional<Zoo> ofZoo = Optional.of(zoo); assertThat(ofZoo.get().getClass()).isEqualTo(Zoo.class); }
ofNullable
ofNullable()では引数で指定したクラスのOptionalを返します。
of()との違いは引数で指定する値はnullでも可です。
@Test public void ofNullable() { Zoo zoo = new Zoo("zoo"); Optional<Zoo> ofNullableZoo = Optional.ofNullable(zoo); assertThat(ofNullableZoo.get().getClass()).isEqualTo(Zoo.class); Zoo zooNull = null; Optional<Zoo> ofNullableZooNull = Optional.ofNullable(zooNull); assertThat(ofNullableZooNull.isPresent()).isFalse(); }
get
get()でOptionalから値を取得します。
値が存在する場合は値を返し、存在しない場合はNoSuchElementExceptionをスローします。
値が存在する場合。
@Test public void get() { Optional<String> any = Stream.of("aaa", "bbb", "ccc") .filter(s -> s.endsWith("bbb")) .findAny(); String get = any.get(); assertThat(get).isEqualTo("bbb"); }
値が存在しない場合。
@Test(expected = NoSuchElementException.class) public void getWithException() throws Exception { Optional<String> any = Stream.of("aaa", "bbb", "ccc") .filter(s -> s.endsWith("ddd")) .findAny(); String get = any.get(); }
getAsInt
getAsInt()はOptionalIntでのget()の様なもので、
値が存在する場合はintを返し、存在しない場合はNoSuchElementExceptionをスローします。
同様にOptionalLongのgetAsLong()、OptionalDoubleのgetAsDouble()があります。
@Test public void getAsInt() { OptionalInt intAny = IntStream.rangeClosed(1, 10) .filter(i -> i == 3) .findAny(); int getInt = intAny.getAsInt(); assertThat(getInt).isEqualTo(3); }
ifPresent
ifPresent()でOptionalに値が存在する場合は、引数で指定した関数を実行します。
Consumerインターフェースを引数に取るので、値を返さない関数を記述します。
@Test public void ifPresent() { Optional<String> any = Stream.of("aaa", "bbb", "ccc") .filter(s -> s.endsWith("bbb")) .findAny(); any.ifPresent(System.out::println); }
isPresent
isPresent()でOptionalに値が存在する場合はtrue、それ以外の場合はfalseを返します。
@Test public void isPresent() { Optional<String> any = Stream.of("aaa", "bbb", "ccc") .filter(s -> s.endsWith("bbb")) .findAny(); assertThat(any.isPresent()).isTrue(); }
orElse
orElse()でOptionalに値が存在する場合はその値を返し、存在しない場合は引数で記述した値を返します。
値が存在しない場合のデフォルト値のような感じです。
@Test public void orElse() { Optional<String> any = Stream.of("aaa", "bbb", "ccc") .filter(s -> s.endsWith("ddd")) .findAny(); String get = any.orElse("fallback"); assertThat(get).isEqualTo("fallback"); OptionalInt intAny = IntStream.rangeClosed(1, 10) .filter(i -> i == 12) .findAny(); int getInt = intAny.orElse(-1); assertThat(getInt).isEqualTo(-1); }
orElseGet
orElseGet()でOptionalに値が存在する場合はその値を返し、存在しない場合は引数で記述した関数の結果を返します。
Supplierインターフェースを引数に取るので、何らかの値を返す関数を記述します。
@Test public void orElseGet() { Optional<String> any = Stream.of("aaa", "bbb", "ccc") .filter(s -> s.endsWith("ddd")) .findAny(); String get = any.orElseGet(() -> "eee"); assertThat(get).isEqualTo("eee"); OptionalInt intAny = IntStream.rangeClosed(1, 10) .filter(i -> i == 12) .findAny(); // IntSupplier int getInt = intAny.orElseGet(() -> -1); assertThat(getInt).isEqualTo(-1); }
orElseThrow
orElseGet()でOptionalに値が存在する場合はその値を返し、存在しない場合は引数で記述した関数で生成された例外をスローします。
Supplierインターフェースを引数に取るので、スローする例外を生成する処理を記述します。
@Test(expected = IllegalArgumentException.class) public void orElseThrow() throws Exception { Optional<String> any = Stream.of("aaa", "bbb", "ccc") .filter(s -> s.endsWith("ddd")) .findAny(); String get = any.orElseThrow(IllegalArgumentException::new); }
filter
filter()で引数に記述した条件を満たす場合値を返し、それ以外は空のOptionalを返します。
Predicateインターフェースを引数に取るので、booleanを返す関数を記述します。
@Test public void filter() { String text = Stream.of("aa", "ab", "ba", "bb") .filter(s -> s.endsWith("b")) .findAny() .filter(s -> s.startsWith("a")) .get(); assertThat(text).isEqualTo("ab"); }
map
map()でOptionalに値が存在する場合は引数で記述した関数の結果を返し、存在しない場合は空のOptionalを返します。
Functionインターフェースを引数に取るので、何らかの値を返す関数を記述します。
@Test public void map() { String text = Stream.of("aa", "ab", "ba", "bb") .filter(s -> s.equals("ab")) .findAny() .map(String::toUpperCase) .get(); assertThat(text).isEqualTo("AB"); }
flatMap
flatMap()でOptionalに値が存在する場合は、引数で記述したOptionalを返す関数の結果を返し、存在しない場合は空のOptionalを返します。
Optionalの結果が返りますが、flatMap()ではOptional<Optional<String>>のようにラップされないようになります。
@Test public void flatMap() { Optional<String> text = Stream.of("aaa", "bbb", "ccc") .max(Comparator.naturalOrder()); Optional<Integer> num = Stream.of(3, 5, 2, 8) .max(Comparator.naturalOrder()); Optional<String> max = text.flatMap(s -> num.flatMap(i -> Optional.of(s + i)) ); String result = max.get(); assertThat(result).isEqualTo("ccc8"); }
テストコードは下記に上げました。
終わり。