Guice で generics の仮型引数の実体 class (ParameterizedType) を上手いこと inject する
タイトルだけでは何を言っているかよくわかりませんが,
public class Foo<T> { private Class<T> clazz; ... public void something() { // 例えばここで clazz を使って何らか処理をする System.out.println(clazz); } }
みたいな class があるときに,ここの clazz
に T
の実際の class (ParameterizedType) を上手いこと inject したいと言う話題です.
結論から言うと,TypeLiteral<T>
を使うと所望の動作を実現できます.
public class Foo<T> { private Class<T> clazz; @Inject @SuppressWarnings("unchecked") public Foo(TypeLiteral<T> type) { this.clazz = (Class<T>) type.getRawType(); } public void something() { // 例えばここで clazz を使って何らか処理をする System.out.println(clazz); } }
こういう風に TypeLiteral
を inject してから,TypeLiteral#getRawType()
を使うと ParameterizedType を取得することが可能となります.
でもって,
public class App { @Inject private Provider<Foo<Integer>> foo; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { } }); App main = injector.getInstance(App.class); main.foo.get().something(); // => class java.lang.Integer } }
というように使うと,コード中のコメントで書いたように class java.lang.Integer
,つまり T
の実態の class が inject されている様子を見れてめでたしめでたし.
この記事の例では Provider<T>
と Constructor Injection でやりましたが,bind()
を使う方法でも実現可能でしょう.
補足
この記事の例だと何が嬉しいか良くわからん感じですが,どこかで役に立つタイミングが来るはず…… (というか今日実際に役に立つタイミングが来たのだけれど,その例をそのまま出すわけにもゆかないし,汎用的な例を考えたものの良いのが思いつかなかった)