GCPのJavaライブラリに見るenumの使い方

今までJavaを書いてきた中で、定数クラスをゼロから設計した経験があまり多くなく、今でもよく迷ってしまう。そこで、実際のコードで定数定義をどのように行っているのかを見てみた。

以下のコードは、Google CloudのJavaライブラリで使われているもの。ソースコードGitHubで見られる。

public interface Storage extends Service<StorageOptions> {
...
  enum PredefinedAcl {
    AUTHENTICATED_READ("authenticatedRead"),
    ALL_AUTHENTICATED_USERS("allAuthenticatedUsers"),
    PRIVATE("private"),
    PROJECT_PRIVATE("projectPrivate"),
    PUBLIC_READ("publicRead"),
    PUBLIC_READ_WRITE("publicReadWrite"),
    BUCKET_OWNER_READ("bucketOwnerRead"),
    BUCKET_OWNER_FULL_CONTROL("bucketOwnerFullControl");

    private final String entry;

    PredefinedAcl(String entry) {
      this.entry = entry;
    }

    String getEntry() {
      return entry;
    }
  }
...
}

Storageインターフェースの中に、PredefinedAclという定数クラスが定義されている。これらは、Google Cloud Storageバケットアクセス制御リストの内容に等しい。1 それぞれの項目名は単なるStringにすぎないが、それをenum型の中にまとめることで、typoを防ぎ、容易に参照できるようにしている。
ちなみに、この中にはGoogle Cloud Javaのコードの中で一度も参照されていない定数もあるようだ。定数をどこまで記述するかは好みにもよるが、今後の拡張性を考えて、アクセス制御リストの内容を全て列挙しておくことにしたのだろう。

列挙されたプロパティの下にはコンストラクタがある。これが何をしているのかを理解するのに時間がかかったが、このコンストラクタはコンパイル時に使用され、String型の値をもとにenum型の定数を作る役割を担っている。値を中に保持する必要がない場合は、コンストラクタを書かなくても問題ない。なお、このコンストラクタは外からはアクセスできない。万が一publicにしてしまうと、処理中に新しい定数を加えることができてしまう。

その下の getEntry は、それぞれの定数の実際の値を取り出すときに必要となる。 Storage.PredefinedAcl.PUBLIC_READ というように呼び出すだけだと、返り値は PredefinedAcl 型となる。"public_read" という中のStringを取り出したい場合は Storage.PredefinedAcl.PUBLIC_READ.getEntry() と書く。

なお、enumの中の値は安易に取り出さない方がよい。そもそもenumというのは、中にある実際の値を気にせず、コード内で定数値をより簡潔・安全に取り扱えるところに意義がある。 例えば、enumの定数を別の変数に代入して、それを条件分岐に使いたいという場合、別に中の値を取り出す必要はない。その場合は、上記の例でいえば PredefinedAcl 型の変数として扱えば良い。

この記事を書くうえでいろいろな既存の記事を調べたが、やはり公式のチュートリアルが一番わかりやすくて参考になった。英語の記事の方もそこまで読みにくくないので、迷った人はぜひ一度読んでほしい。

docs.oracle.com

docs.oracle.com


  1. ALL_AUTHENTICATED_USERS が表の中にないが、同じページの他の部分で言及があり、「全てのGoogleアカウントに対しOWNER権限を付与する」とのこと。誤って使うと危険すぎるので表に載せなかったのだろう。