無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

タイプセーフな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 ); // 型があわない場合コンパイルエラー

戦略

  • 値の型はキーごとに決まるので、キーの型パラメータで値の型を指定できるようにする。
    • Enumを使いたいところだけど、型パラメータが持てないようなので専用のクラスを作る。
  • PropertiesのAPIGenericsを使って、キーにあった値しか指定できないように制限する。

実装

まずはキーのクラス。専用のクラスを作成して型パラメータを指定できるようにします。型パラメータでは値の取り得る型を指定。

/**
 * プロパティのキー
 * @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 );
    }
}

これで、用件で挙げたコードが期待通りコンパイルエラーになるわけです。