Java Generics and Collections
今日は時間が無かったけど電車の中で第三部を読んでみた。ワイルドカードを使ったイミュータブルの試みというのが面白い。
public class Test {
public List<? extends Integer> getResult() {
return Arrays.asList(1, 2, 3);
}
public static void main(String[] args) {
Test test = new Test();
List<? extends Integer> list = test.getResult();
list.add(1);
}
}
$ javac -Xlint:unchecked Test.java Test.java:11: シンボルを見つけられません。 シンボル: メソッド add(int) 場所 : java.util.Listの インタフェース list.add(1); ^ エラー 1 個
始めは正直、意味不明だった。"extends Integer"って、Integerはfinalですが、とか、List<? extends Integer>だったらIntegerと、そのサブタイプのインスタンスは入れられるんじゃないの? とか。
List<? extends Integer>っていうのは、仮にIntegerが継承可能でSubIntegerとかがあったら、List<SubInteger>の参照も保持できるってことだ。ここにIntegerのインスタンスを放り込むのは当然エラー(もちろん逆、つまりList<Integer>にSubIntegerを放り込むならok)。だからコンパイルエラーなのだ。で、それを利用してlistを変更不可にしているというわけだ。
もちろん、資料にもあるようにclear()とかremove()が防げるわけじゃないし、nullは素通りするので、これは止めましょうと書いてある。ただGenericsの理解を深めるという点では面白い例だ。
もちろんキャストで回避できるのだけど、unchecked警告が出る。
public static void main(String[] args) {
Test test = new Test();
List<Integer> list = (List<Integer>)test.getResult();
list.add(1);
}
$ javac Test.java
注: Test.java の操作は、未チェックまたは安全ではありません。
注: 詳細については、-Xlint:unchecked オプションを指定して再コンパイルしてください。
$ javac -Xlint:unchecked Test.java
Test.java:10: 警告: [unchecked] 無検査キャストです
検出値 : java.util.List<capture of ? extends java.lang.Integer>
期待値 : java.util.List<java.lang.Integer>
List<Integer> list = (List<Integer>)test.getResult();
これって、javacもデフォルトだと控えめに報告するから軽く見ていたのだけど(何せlintだし)、この資料を読んでみて、一部の例外を除けば、基本的には全部つぶしておかないといけないものだというのが良く分かった。さもないと、思わぬところで(その場所では明示的にはキャストをしていない)、原因不明のClassCastExceptionが起きてしまう。





