JDTコンパイラならOKだけど、純正コンパイラじゃ通らない記述

久々にはまった。。。
以前にも書きましたが、JDTコンパイラとSunの純正コンパイラはいろいろ微妙に振る舞いが違ってます。

01:public class Sample {
02:    public static void main(String[] args) {
03:        List list = new ArrayList();
04:        list.add(1L);
05:        list.add(2L);
06:        List<Object[]> list2 = list;
07:        for(Object o : list2) {
08:            System.out.println(o);
09:        }
10:    }
11:}

前回判明したケースは、コンパイル時点で発覚したのですが、
上記コードは、JDTコンパイラでは正しく動作するのですが、Sunの純正コンパイラでは、
ClassCastExceptionが発生してしまいます。

開発環境では普通に動くので見つけるのに苦労しました。
悪いのは6行目の型が一致しない型総称参照への代入ですね。

「そんなのするなよ」と言われればその通りなんですが、
やっちゃったものは仕方なし^^;
で、なんで振る舞いが異なるかっていうと、jadでリバースをかければ一目瞭然。

JDTコンパイラのclassファイルからリバース

01:    public static void main(String args[])
02:    {
03:        List list = new ArrayList();
04:        list.add(Long.valueOf(1L));
05:        list.add(Long.valueOf(2L));
06:        List list2 = list;
07:        Object o;
08:        for(Iterator iterator = list2.iterator(); iterator.hasNext(); System.out.println(o))
09:            o = iterator.next();
10:    }

Sun純正コンパイラのclassファイルからリバース

01:    public static void main(String args)
02:    {
03:        ArrayList arraylist = new ArrayList();
04:        arraylist.add(Long.valueOf(1L));
05:        arraylist.add(Long.valueOf(2L));
06:        ArrayList arraylist1 = arraylist;
07:        Object aobj;
08:        for(Iterator iterator = arraylist1.iterator(); iterator.hasNext(); System.out.println(((Object) (aobj))))
09:            aobj = (Object[])iterator.next();
10:    }

拡張forループの一時参照の型が違うから見たいですね。
つまり、、、

  • JDTコンパイラは、拡張forループで指定したクラスの参照として受ける
  • Sun純正コンパイラは、型総称の型(の方が具象的な場合は)型総称の型として受ける

というコンセプトで拡張forループは展開するようです。

う〜ん、コンパイラの実装としてはJDTコンパイラの方がいい気がするんだけどね。
ちなみに、jadの結果を見ると、メソッド内の一時参照変数は具象クラスが明確な場合には、
あえて、具象クラスの型で受けるようですね。
このあたりもどうだろうという気がせんことはないのですが、こっちの方が早く動作するんでしょうね。
JDTコンパイラはとにかく高速にコンパイルする必要が有るので、あまり最適化はしないってこことでFAですかね?

ちなみに、前回(と言ってもはるか昔)書いた日記は、
完全なガセでした。
普通に動いてくれちゃってます。
う〜ん、徹夜作業中だったから見間違えたのかしら。。。