覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

com.google.common.base.Optional

Guavacom.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]



関連エントリ