SunのコンパイラとEclipse JDTで動きが違う(2)
SunのコンパイラとEclipse JDTで動きが違う - しげるメモでも言及してますが、SunのjavacとEclipseのJDTでは微妙に内部クラス周りの動作に差異がある模様。
java.lang.Class.getCanonicalName()の動作を確認してる最中で、また微妙に動きが違うものを発見。
とりあえず、
package com.example.reify; public class Generic<T> { class Inner {} static class Applied extends Generic<String> {} public static void main(String[] args) { Class<?> aInner = Applied.Inner.class; Class<?> gInner = Generic.Inner.class; System.out.println(aInner); System.out.println(gInner); System.out.println(aInner == gInner); } }
上記を実行すると、こんな感じになります。
class com.example.reify.Generic$Inner class com.example.reify.Generic$Inner true
Applied.Inner.classは具現化されないので、実際にはGeneric.Innerを参照している模様。ここまでは良かったのですが、Applied.Inner.classを評価した結果がjavacとJDTで妙に異なりました。
public class Generic<T> { class Inner {} static class Applied extends Generic<String> {} public static void main(String[] args) { Class<Applied.Inner> just = Applied.Inner.class; Class<Generic.Inner> raw = Applied.Inner.class; Class<Generic<String>.Inner> prm = Applied.Inner.class; } }
これをSunのjavacとEclipseのJDTでそれぞれコンパイルしてみたところ、下記のようになりました。
変数の型 | Sun javacでの評価 | Eclipse JDTでの評価 |
---|---|---|
Class<Applied.Inner> | コンパイルエラー | OK |
Class<Generic.Inner> | OK | コンパイルエラー |
Class<Generic<String>.Inner> | コンパイルエラー | OK |
javacのほうは、なんとClass<Applied.Inner>にApplied.Inner.classが代入できないという気持ち悪い動きをして、Class<Generic.Inner>に代入できていることから、Applied.Inner.classはGeneric.Inner.classと読んで評価している模様です。
JDTのほうは、Sunと逆でClass<Applied.Inner>とClass<Generic<String>.Inner>に代入できていることから、Applied.Inner型はGeneric
ちなみに、言語仕様的には
Class<?> klass = Generic<String>.Inner.class;
というような型引数を伴う型のクラスリテラル式は文法的にアウトです*1。これはjavacでもJDTでも共通していました。
そうすると、今回はClass<Generic<String>.Inner>型のクラスリテラルを作れてしまうJDTのほうが完全におかしな気がする…