genericsにクラス情報を持たせる

genericsコンパイラ向けの情報で、実行時にはパラメータクラスの情報は残っていない。ということで、実行時にクラス情報を持たせるべく、インスタンス生成時にパラメータとしてクラスを渡し、インスタンス変数として保持して使いたい!

という流れで先日のエントリid:unageanuさんからコメントを頂いたので、続けて考えてみた。

ID<Foo> fooId2 = new ID<Foo>(”/a”, Foo.class);

これでも一応要件は満たせるんですが、冗長なんですよね。Fooが3回も出てくるし。「o instanceof ID」とかできるようになって欲しいものです。
コメント - 型パラメータの扱いの困ったところ。

上記例だととFoo.classの2回記述するのが面倒なので、Foo.classを指定してgenericsインスタンスを取得するようにしてみた。

使い方

コンストラクタではなく、staticメソッドでインスタンスを生成するようにしてみた。実装も"Hoge"を2回記述するだけなので、よりシンプルかと。

public class IDTest {
    public static void main(String[] args) {
        ID<Hoge> hogeID = ID.newInstance(Hoge.class, "123");
        ID<Hoge> hogeID2 = ID.newInstance(Hoge.class, "123");
        ID<Fuga> fugaID = ID.newInstance(Fuga.class, "123");
        System.out.println(hogeID.equals(hogeID2)); //prints "true"
        System.out.println(hogeID.equals(fugaID));  //prints "false"
    }
}

class Hoge {}
class Fuga {}

抽象総称クラス

折角なので、抽象クラスを用意してコンストラクタをprotectedとした。とりあえずequalsメソッドのみ実装。

public abstract class AbstractGenerics<T> {
    public final Class<T> type;
    
    protected AbstractGenerics(Class<T> type) {
        this.type = type;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof AbstractGenerics) {
            AbstractGenerics that = (AbstractGenerics) obj;  //warning
            return this.type.equals(that.type);
        }
        return false;
    }
}

IDクラス

継承し、IDを返却するstaticでpublicなメソッドを用意し、普通はコレでインスタンスを生成するようにした。

public class ID<T> extends AbstractGenerics<T> {
    public final String key;

    /** 隠蔽されたコンストラクタ */    
    protected ID(Class<T> type, String key) {
        super(type);
        this.key = key;
    }
    
    /** ID<T>のインスタンスを生成 */
    public static <T> ID<T> newInstance(Class<T> type, String key) {
        return new ID<T>(type, key);
    }
    
    /** キーまたはクラスがequalかを返却。nullはとりあえず無視。 */
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof ID) {
            ID that = (ID) obj;  //warning
            return this.key.equals(that.key) && super.equals(obj);
        }
        return false;
    }
}

結果

動いた。

true
false

この使い方の場合、型を指定しないインスタンスは生成できないように規制することもできるので、よりお堅いインタフェースを作ることができると思う。

これは使っていこう。

"newInstance"っていうメソッド名もちょっと長いので、格好いい名前はないだろうかな。