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

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

型パラメータで指定されたクラスを取得する。

クラス定義の段階で型パラメータが決まっている場合(以下のようなクラスが該当)、Classから型パラメータで指定された型を得ることができます。(↓のクラスだと、HogeSet.classからSetの型パラメータであるString.classがとれる。)

// クラス定義の段階でSetのパラメータを決めているSetの実装。
class HogeSet implements Set<String> {
...

手順

  1. Class#getGenericInterfaces(),Class#getGenericSuperclass()で親クラスのTypeを取得できます。
  2. ParameterizedTypeの場合、ParameterizedType#getActualTypeArguments()で型パラメータが取得できます。
    • このとき、クラス定義の段階で型パラメータが決まっている場合、パラメータで指定されたClassが返されます。(クラスがパラメータ化されていればParameterizedTypeになるのかも。未検証)
    • そうでない場合は、TypeVariableになります。

この手順に従って、指定オブシェクトから型パラメータを取得する関数を書いてみました。

関数の実装。

引数でオブジェクトとクラスを受け取って、クラスの型パラメータを取り出します。

/**
 * オブジェクトが実装するクラスorインターフェースに指定された型パラメータを取得する。
 *
 * @param obj オブジェクト
 * @param type 取得対象のパラメータの所有者であるクラス
 * @return 型パラメータ
 */
static Type[] getGenericType( Object obj, Class type ) {
    Class cl = obj.getClass();
    while ( cl != null  ) {
        Type t = cl.getGenericSuperclass();
        Type[] res = _getGenericType( type, t );
        if ( res != null ) {
            return res;
        }
        for ( Type t2 : cl.getGenericInterfaces() ) {
            res = _getGenericType( type, t2 );
            if ( res != null ) {
                return res;
            }
        }
        cl = cl.getSuperclass();
    }
    return new Type[0];
}
private static Type[] _getGenericType( Class type, Type t ) {
    if ( t != null
        && t instanceof ParameterizedType
        && type.equals( ((ParameterizedType) t).getRawType()  ) ) {
        return type != null ? ((ParameterizedType) t).getActualTypeArguments() : new Type[0];
    }
    return null;
}

サンプル

テスト用クラス。

// パラメータ化されたインターフェイス
static interface InterfaceA<X, Y>{}
static interface InterfaceB<X>{}

// 親の親のクラス
static class ParentParent<X, Y, Z>{}

// 親のクラス
static class Parent<X, Y>
extends ParentParent<X, Y, Boolean>{}

// テスト用クラス
static class Child<X>
extends Parent<X, String>
implements InterfaceA<X, Integer>, InterfaceB<X> {}

サンプル。

Child<Date> child = new Child<Date>();

System.out.println( "\n--- Parent" );
for ( Type t : getGenericType( child, Parent.class) ) {
    System.out.println( t );
}
System.out.println( "\n--- ParentParent" );
for ( Type t : getGenericType( child, ParentParent.class) ) {
    System.out.println( t );
}
System.out.println( "\n--- InterfaceA" );
for ( Type t : getGenericType( child, InterfaceA.class) ) {
    System.out.println( t );
}
System.out.println( "\n--- InterfaceB" );
for ( Type t : getGenericType( child, InterfaceB.class) ) {
    System.out.println( t );
}

実行結果です。XとかYになっているところはTypeVariableが返されています。実際の型は取得できません。

--- Parent
X
class java.lang.String

--- ParentParent
X
Y
class java.lang.Boolean

--- InterfaceA
X
class java.lang.Integer

--- InterfaceB
X

インスタンスの生成時に渡した型パラメータが取りたい!

調べた範囲では、インスタンスの生成時に渡した型パラメータ(上のサンプルでXとかYになっているもの。実際にはDateが指定されている。)は取得できない様子。「クラスじゃなくてインスタンスが持つパラメータだからとれない」とか、そういう仕様なのかなー。だったらField#get()みたいなのを用意してくれてもいいような気がする。なにか見落としてるのかも。