分からない!ジェネリクス
昨日の記事を書いたのは、Google Guiceを使うときにジェネリクスのクラスインスタンスを指定しようとしたがその方法がわからなかったからだった。
ジェネリクス型を使うのは非常に便利なのだが、こうやって困ったときにどうしたらいいのか分からなくなる。
困った事
Guiceのbind関数の引数にクラスインスタンスを渡し、返ってきたオブジェクトに対してtoInstance関数を呼び、引数にそのクラスのオブジェクトを渡す。String型ならこんな感じ。
bind(String.class).toInstance("hoge");
このbind関数にList<String>のクラスインスタンスを渡したい。しかしそもそもList<String>のクラスインスタンスとは何なのか分かっていない。まず、List<String>とListというのは同じクラスインスタンスを持つのではないかと推測し、それを調べるために以下のコードを実行してみた。
public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); System.out.println(list.getClass().equals(ArrayList.class)); } }
結果、trueが返ってきた。ジェネリクス型はコンパイラが静的に解析してエラーを出すだけで、クラスインスタンスそのものは同じなようだ。そこで以下のコードを書いてみた。
Test.java
public class Test { public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { List<String> list = new ArrayList<String>(); list.add("AAA"); list.add("BBB"); bind(List.class).toInstance(list); } }); injector.getInstance(Hoge.class).hoge(); } }
Hoge.java
public class Hoge { @Inject private List<String> list; public void hoge() { for(String str : list) { System.out.println(str); } } }
この2つのクラスはコンパイルが通るが、実行時にGuiceが以下のような例外を出す。Guiceがしっかりと型チェックをしているようだが、java.util.List
1) No implementation for java.util.List<java.lang.String> was bound. while locating java.util.List<java.lang.String> for field at Hoge.list(Hoge.java:6) while locating Hoge
まあジェネリクス型を使わなければコンパイルは通るのだが、型チェックしたいしなんとかしてコンパイラを黙らせたい。何かいい方法は無いものか...。安直に以下のコードで黙ってくれるかと思ったがコンパイルエラーになった。
@Override protected void configure() { List<String> list = new ArrayList<String>(); list.add("AAA"); list.add("BBB"); bind(list.getClass()).toInstance(list); }
The method toInstance(capture#1-of ? extends List) in the type LinkedBindingBuilder<capture#1-of ? extends List> is not applicable for the arguments (List<String>)
何がどうなってるのやら。インターフェース型かクラス型か、その辺りで引っかかっているような気もするなあ。眠いのでまた明日。