Guavaのcom.google.common.base.Optional
の利用サンプルです。
Optionalを使って値をラップすることで、nullを利用せずに値が存在しないことを表現することができます。
以下のようなAPIを提供します
Optional#of
- 指定された参照を保持するインスタンスを生成Optional#absent
- 特定の参照を保持しないインスタンスを生成(存在しない状態を表現)Optional#isPresent
- null以外のインスタンスを保持している場合にtrueを返却Optional#get
- 保持しているインスタンスを返却Optional#or
- 参照を保持していない場合に引数で指定した値をデフォルト値として返却
MapのvalueにOptionalを使ってみる
MapのvalueにOptionalを使わない場合と使う場合で違いをみます
Optionalを使わないMap
コードのコメントに書いていますが、値にnullを指定したkeyと、そもそも登録がないkeyの区別をつけることができません
import java.util.HashMap; import java.util.Map; public class NotOptionalMap { /** Optionalを使わないMap */ private static final Map<String, String> map = new HashMap<>(); static { map.put("key1", "Koizumi Juntarou"); map.put("key2", null); map.put("key3", "Kan Naotarou"); } public static void main(String[] args) { // key1とkey99の登録状態(nullの意味が異なること)を区別できない System.out.println("key1 : " + map.get("key1")); System.out.println("key2 : " + map.get("key2")); System.out.println("key3 : " + map.get("key3")); System.out.println("key99: " + map.get("key99")); } }
■実行結果
key1 : Koizumi Juntarou key2 : null key3 : Kan Naotarou key99: null
Optionalを使ったMap
Optional#absent
をvalueに利用することで対象のkeyに対しての値が存在しない状態を表現することができます
import java.util.HashMap; import java.util.Map; import com.google.common.base.Optional; public class OptionalMap { /** Optionalを使ったMap */ private static final Map<String, Optional<String>> map = new HashMap<>(); static { map.put("key1", Optional.of("Koizumi Junko")); map.put("key2", Optional.absent()); map.put("key3", Optional.of("Kan Naoko")); } public static void main(String[] args) { // key2とkey99の登録状態を区別できる(isPresentを使って値の有無を判定) System.out.println("key1 : " + getIsPresent("key1")); System.out.println("key2 : " + getIsPresent("key2")); System.out.println("key3 : " + getIsPresent("key3")); System.out.println("key99: " + getIsPresent("key99")); System.out.println("###"); // key2とkey99の登録状態を区別できる(orを使って値が無い場合デフォルト値を返す) System.out.println("key1 : " + getOr("key1")); System.out.println("key2 : " + getOr("key2")); System.out.println("key3 : " + getOr("key3")); System.out.println("key99: " + getOr("key99")); } /** * Optional#isPresentを使って判定して特定の文字列を返す */ private static String getIsPresent(String key) { Optional<String> value = map.get(key); if (value == null) { return "null"; } if (!value.isPresent()) { return "none"; } return value.get(); } /** * Optional#orを使って値が無い場合デフォルト値を返す */ private static String getOr(String key) { Optional<String> value = map.get(key); if (value == null) { return "null"; } return value.or("default_value"); } }
■実行結果
key1 : Koizumi Junko key2 : none key3 : Kan Naoko key99: null ### key1 : Koizumi Junko key2 : default_value key3 : Kan Naoko key99: null
Optional#asSetで唯一の要素を持つImmutableなSetを生成
Optional#asSet
を利用することで要素を1つだけ持つ不変なSetを生成することができます
■サンプルコード
import java.util.Set; import com.google.common.base.Optional; public class OptionalClient1 { public static void main(String[] args) { // Optional#asSetで要素を1つだけ持つImmutableなSetを生成できる。 Set<String> nameSet = Optional.of("Mario").asSet(); System.out.println("Set Elements: " + nameSet); // 追加操作等をしようとすると例外がスローされる try { nameSet.add("Luigi"); } catch (UnsupportedOperationException e) { e.printStackTrace(); } } }
■実行結果
Set Elements: [Mario] java.lang.UnsupportedOperationException at java.util.AbstractCollection.add(AbstractCollection.java:262) at sample.optional.OptionalClient1.main(OptionalClient1.java:14)
Optionalでnullを扱う
Optionalを生成する場合に、Optional#of
では nullを引数に取ることができません。(NullPointerException
がスローされます)
しかし、 Optional#fromNullable
によりnullを指定してOptionalを生成することができます。
nullの値をラップしたOptionalの操作の結果は以下のような状態となります
Optional#get
- IllegalStateException がスローされるOptional#isPresent
- false が返るOptional#or
- orメソッドに渡した引数の値が返るOptional#orNull
- nullが返る
■サンプルコード
import com.google.common.base.Optional; public class OptionalClient2 { public static void main(String[] args) { String emptyStr = ""; String notNullStr = "not null"; String nullStr = null; System.out.println("## Execute Optional#of(Empty String)"); Optional<String> emptyOp = Optional.of(emptyStr); System.out.println("## Execute Optional#of(Not null String)"); Optional<String> notNullOp = Optional.of(notNullStr); System.out.println("## Execute Optional#of(null)"); // Optional#of は nullを引数にとってOptionalを作ることはできない try { Optional<String> nullOp = Optional.of(nullStr); } catch (NullPointerException e) { System.out.println("!! Failed. Execute Optional#of(null) " + e.getMessage()); } System.out.println("\n------------------------\n"); System.out.println("## Execute Optional#fromNullable(Empty String)"); Optional<String> emptyOp2 = Optional.fromNullable(emptyStr); System.out.println("## Execute Optional#fromNullable(Not null String)"); Optional<String> notNullOp2 = Optional.fromNullable(notNullStr); System.out.println("## Execute Optional#fromNullable(null)"); // Optional#fromNullable は nullを引数にとってOptionalを作ることができる Optional<String> nullOp2 = Optional.fromNullable(nullStr); System.out.println("\n####### Optional -- Empty String"); System.out.println("Optional#absent<Empty String>: [" + emptyOp2.isPresent() + "]"); System.out.println("Optional#get<Empty String>: [" + emptyOp2.get() + "]"); System.out.println("Optional#or<Empty String>: [" + emptyOp2.or("default-value") + "]"); System.out.println("Optional#orNull<Empty String>: [" + emptyOp2.orNull() + "]"); System.out.println("\n####### Optional -- Not null String"); System.out.println("Optional#isPresent<Not null String>: [" + notNullOp2.isPresent() + "]"); System.out.println("Optional#get<Not null String>: [" + notNullOp2.get() + "]"); System.out.println("Optional#or<Not null String>: [" + notNullOp2.or("default-value") + "]"); System.out.println("Optional#orNull<Not null String>: [" + notNullOp2.orNull() + "]"); System.out.println("\n####### Optional -- null String"); System.out.println("Optional#isPresent<null>: [" + nullOp2.isPresent() + "]"); try { System.out.println("Optional#get<null>: [" + nullOp2.get() + "]"); // nullで生成したOptionalからgetで値を取得できない } catch (IllegalStateException e) { System.out.println("!! Failed. Execute Optional#get<null> " + e.getMessage()); } System.out.println("Optional#or<null>: [" + nullOp2.or("default-value") + "]"); System.out.println("Optional#orNull<null>: [" + nullOp2.orNull() + "]"); // Optional#orNull なら nullを取り出せる } }
■実行結果
## Execute Optional#of(Empty String) ## Execute Optional#of(Not null String) ## Execute Optional#of(null) !! Failed. Execute Optional#of(null) null ------------------------ ## Execute Optional#fromNullable(Empty String) ## Execute Optional#fromNullable(Not null String) ## Execute Optional#fromNullable(null) ####### Optional -- Empty String Optional#absent<Empty String>: [true] Optional#get<Empty String>: [] Optional#or<Empty String>: [] Optional#orNull<Empty String>: [] ####### Optional -- Not null String Optional#isPresent<Not null String>: [true] Optional#get<Not null String>: [not null] Optional#or<Not null String>: [not null] Optional#orNull<Not null String>: [not null] ####### Optional -- null String Optional#isPresent<null>: [false] !! Failed. Execute Optional#get<null> Optional.get() cannot be called on an absent value Optional#or<null>: [default-value] Optional#orNull<null>: [null]