genericsにクラス情報を持たせる
genericsはコンパイラ向けの情報で、実行時にはパラメータクラスの情報は残っていない。ということで、実行時にクラス情報を持たせるべく、インスタンス生成時にパラメータとしてクラスを渡し、インスタンス変数として保持して使いたい!
という流れで先日のエントリにid:unageanuさんからコメントを頂いたので、続けて考えてみた。
ID<Foo> fooId2 = new ID<Foo>(”/a”, Foo.class);これでも一応要件は満たせるんですが、冗長なんですよね。Fooが3回も出てくるし。「o instanceof ID
」とかできるようになって欲しいものです。
コメント - 型パラメータの扱いの困ったところ。
上記例だと
使い方
コンストラクタではなく、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
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"っていうメソッド名もちょっと長いので、格好いい名前はないだろうかな。