ジェネリクスを利用してキャストを減らす
ひが氏のブログを読んでいたら、 AnnotationとGenerics に以下のような記述があった。
Component component = Foo.class.getAnnotation(Component.class);はOKだけど。
Class clazz = Foo.class; Component component = clazz.getAnnotation(Component.class);はNG。
(以下省略)
コレをみて、「あ、Java の Generics でもメソッド引数のテンプレート型を戻り値にできるんだ」と初めて知りました。C++ のテンプレートだと当たり前のように出来ていたのですが、Java の Generics では出来ないものだと思い込んでました。
C++ の場合ですと、abs や pow といった関数が例としてよく見かける気がします。
以下が、C++ の関数テンプレート例です。
=== C++の場合の関数テンプレート === // 多分こんな感じ template<class T> T abs(T x) { return x<0 ? x * -1 : x; }
関数の戻り値型は関数の引数によって決定されます。 abs(123) とすれば、戻り値の型は int になりますし、abs(123456789L) とすれば long になります。
Java の場合は Class クラスと組み合わせることで、引数で指定した型のオブジェクトを戻り値とすることも可能です。
これを利用すれば、今まではキャストを行なわざるをえなかったケースでも、キャストを行なわないで処理をすることが可能になります。
たとえば、今までキャストを行なっていた例として汎用的に使用されるファクトリクラスを示します。
// DaoFactory はフレームワークで用意されている、汎用的な部品 // 対して、WDao は特定のシステムで作成された、業務の部品 DaoFactory daoFactory = DaoFactory.newInstance(); WDao wDao = (WDao)daoFactory.getDao(WDao.class); wDao.insertNono(・・・);
daoFactory#getDao の戻り値は Java の 1.4 までは Object 型にせざるを得ないため、引数で型を指定したとしても、戻り値をキャストしないと使えません。
このため、キャストの型を誤って記述していたとしても、実行するまで気づきませんでした。
そこで、Java の 5.0 からは Generics を使用することでキャストをする必要がなくなります。引数の型を戻り値の型としてしまうことが可能になるからです。
先ほどのファクトリクラスに Generics を使用することで、キャストを不必要にした例を示します。
=== DaoFactory クラス === public class DaoFactory { // インタフェース -> 実装クラス の型情報を保持するマップ private Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>; : : public <T extends Dao> T getDao(Class<T> daoClass) throws InstantiationException, IllegalAccessException { Class<?> implClass = map.get(daoClass); Class<? extends T> resultClass = implClass.asSubclass(daoClass); return resultClass.newInstance(); } : : } === DaoFactory 利用側 === DaoFactory daoFactory = DaoFactory.newInstance(); WDao wDao = daoFactory.getDao(WDao.class); wDao.insertNono(・・・);
上記のように、getDaoの戻り値をキャストすることなく変数で受けることができるようになります。
これは、なかなか便利かも。