タイプセーフなPropertiesを作る。
タイプセーフなPropertiesを作ってみます。
用件
- 整数など任意の型を記録できるjava.util.Propertiesみたいなのを作りたい。
- 値の型はキーごとに決まるので、キーに合わせてタイプセーフにしたい。
- Stringを取り得るキーの場合、値をStringで設定でき、Stringで取り出せる。
- intを取り得るキーの場合、値をintで設定でき、intで取り出せる。
ようはこんな感じにしたい。
Properties props = new Properties(); // キーにあった型の値のみ指定できる。 props.set( Key.STRING_PROPERTY, "hoge" ); props.set( Key.INTEGER_PROPERTY, 0 ); //props.set( Key.STRING_PROPERTY, 1 ); // 型があわない場合コンパイルエラー //props.set( Key.STRING_PROPERTY, new Object() ); // キャストなしに値を取得できる。 String property = props.get( Key.STRING_PROPERTY ); int i = props.get( Key.INTEGER_PROPERTY ); //int i2 = props.get( Key.STRING_PROPERTY ); // 型があわない場合コンパイルエラー //InputStream value = props.get( Key.STRING_PROPERTY ); // 型があわない場合コンパイルエラー
戦略
実装
まずはキーのクラス。専用のクラスを作成して型パラメータを指定できるようにします。型パラメータでは値の取り得る型を指定。
/** * プロパティのキー * @param <T> プロパティが取り得るデータの型 * * @version $Revision:$ * @author $Author:$ */ public class Key<T> { /**文字列を持つキー*/ public static final Key<String> STRING_PROPERTY = new Key<String>( "STRING_PROPERTY" ); /**整数を持つキー*/ public static final Key<Integer> INTEGER_PROPERTY = new Key<Integer>( "INTEGER_PROPERTY" ); /**キー*/ private final String key; /** * コンストラクタ * @param key キー */ private Key( String key ) { this.key = key; } /* 継承元のクラスのJavaDocを参照 */ public boolean equals ( Object obj ) { if ( obj == null ) { return false; } if ( obj instanceof Key ) { Key that = (Key) obj; return that.key == null ? key == null : that.key.equals( key ); } return false; } /* 継承元のクラスのJavaDocを参照 */ public int hashCode () { return key.hashCode(); } }
プロパティクラスの実装は以下。メソッドのAPI上で型の制限を課します。内部的にはObjectとしてMapに記録。
import java.util.HashMap; import java.util.Map; /** * タイプセーフなプロパティ * * @version $Revision:$ * @author $Author:$ */ public class Properties { /*** * 値の格納先 */ private Map<Key<?>, Object> props = new HashMap<Key<?>, Object>(); /** * 値を取得する。 * @param <V> 値の型 * @param key プロパティキー * @return 値 */ public <V> V get( Key<V> key ) { return (V) props.get( key ); } /** * 値を取得する。 * @param <V> 値の型 * @param key プロパティキー * @param defaultValue 値がnullだった場合の初期値 * @return 値 */ public <V> V get( Key<V> key, V defaultValue ) { V value = get( key ); return value == null ? defaultValue : value; } /** * 値を設定する。 * @param <V> 値の型 * @param key プロパティキー * @param value 値 */ public <V> void set( Key<V> key, V value ) { props.put( key, value ); } }
これで、用件で挙げたコードが期待通りコンパイルエラーになるわけです。