読者です 読者をやめる 読者になる 読者になる

No Programming, No Life

新しいNPNLです。http://d.hatena.ne.jp/fumokmm/ から引っ越してきました。

Javaでenumを使ってプロパティファイルのキーを定義する

はじめに

enumでインタフェースが実装できることが分かったので、その応用例の一つとして、Javaenumを使ってプロパティファイルのキーを定義するサンプルを書いてみました。

プロパティファイル読み込みは何が問題か

Javaでの外部プロパティファイルの機構は、

key=value

の形で外部ファイル *.properties にて定義し*1、それをjava.util.Properties を利用して取得する。

// 例
Properties prop = new Properties();
prop.load(...省略...):
String key = "key1"; // ← キーが文字列!
String value = prop.getProperty(key); // ← 文字列のキーを渡してプロパティの値を取得!

ですが、プロパティファイルは、決められた値の設定を記述しておくといったような用途が多いと思いますので、キーは最初から分かっているはずですよね。しかし、上記例のような方法だと、文字列にてキーを引き当てていますので、タイポや、キー変更による実行時に値が取得できないといったようなエラーが懸念されます。

できれば、キーは定数化したい

そんなの簡単じゃないかブラザー!定数で定義すればいいんだろ?

// 定数としてキーを定義
public static final String PROP_KEY_KEY1 = "key1";
public static final String PROP_KEY_KEY2 = "key2";
public static final String PROP_KEY_KEY3 = "key3";
 ・
 ・
 ・

// 使用
String value = prop.getProperty(PROP_KEY_KEY1); // 定数化してプロパティの値を取得!

わぉ、レガシー!・・・これでもまぁ、キーの文字列を直接指定していた場合よりはましですが。

もっといい方法はないか

Javaだったら定数は全てenumを使えばいい。

そうですね、enumを使えばいいんです。こんな感じに。

// enumでキーを定義
enum PropKey {
  key1, key2, key3
}
// 使用
String value = prop.getProperty(PropKey.key1.name()); // enumの要素(の文字列)を使ってプロパティの値を取得!
問題点

ただenumを使った場合、一つだけ問題が残ります。それはプロパティファイルが複数に増えてきた場合のお話です。たとえば、プロパティファイルの読み込みは実際には例外処理などを行う必要があり、煩雑となるので、だいたいユーティリティクラスを作って読み込み処理を実装すると思いますが、その場合のキーをPropKeyのようにしてしまうと、複数のプロパティファイルへの対応できなくなってしまいます。
仕方ないので、キー名を工夫して、name() でキー文字列を取得するのではなく、getKey() などのメソッドを定義して、うまいことキーやファイルを特定するなどの痛々しい努力をするなんてシナリオも考えられます。

// enumで複数プロパティ分のキーを定義
enum PropKey {
  hoge_key1, hoge_key2, hoge_key3, // ← hoge.properties 用のキー
  fuga_key1, fuga_key2, fuga_key3  // ← fuga.properties 用のキー

  public String getKey() {
    return 先頭の "xxx_" を取り除いた文字列;
  }
}
// 使用
String value = PropertyReader.getProp(PropKey.hoge_key1);

そこでenumにインタフェースを持たせて、プロパティファイルのキーを定義してみる

インタフェースの定義

まずはこんなインタフェースを作成

/** プロパティのキーを表現するインタフェース */
interface PropKey {
  Properties getProperties();
  String name();
}
enumの定義

次にこのインタフェースを実装するプロパティのキーとなるenumを定義

/** hoge.properties で利用するキー */
enum HogeKey implements PropKey {
  key1,
  key2,
  ;
  public static final String PATH = "hoge.properties";
  @Override public Properties getProperties(){
    return PropertyReader.getProperties(new File(PATH));
  }
}

/** fuga.properties で利用するキー */
enum FugaKey implements PropKey {
  key1,
  key2,
  ;
  public static final String PATH = "fuga.properties";
  @Override public Properties getProperties(){
    return PropertyReader.getProperties(new File(PATH));
  }
}
読み込みユーティリティの定義

最後は実際にプロパティファイルから値を読み込む

/** プロパティ読み込みクラス */
class PropertyReader {
    ... 省略 ...
  /**
   * プロパティファイルから値を読み込む
   * @param key PropKey
   * @return
   */
  public String getProp(PropKey key) {
    ... 省略 ...
    Properties prop;
    ... 省略 ...
    return prop.getProperty(key.name());
  }
    ... 省略 ...
}

読み込みユーティリティ(PropertyReader)の読み込みメソッドの引数はenumのインタフェース型としています。これにより、プロパティの定義時(enum, *.properties)とプロパティ読み込みのためのキー指定時は明示的に、ユーティリティ内部での実際のプロパティファイル読み込み時は汎用的にキーを扱えるようにしています。

使用例
PropertyReader propReader = new PropertyReader();
propReader.getProp(HogeKey.key1); // => "hoge-value-1"
propReader.getProp(HogeKey.key2); // => "hoge-value-2"
propReader.getProp(FugaKey.key2); // => "fuga-value-2"
ソース

実際に動くサンプルを載せておきます。

補足

おまけで、enumで定義したキーがプロパティに存在しているかのテストも追加してあります。また、一回読み込んだプロパティ(Properties)はキャッシュするようにしています。

*1:もしくはxml形式にすることも可能。