型推論の正解が分からない

自分めも。だけどわかる人教えてください。

public static void main(String...args) {
   Integer a = 1;
   Long b = 2;
   Object result = method(a, b); // resultの型は?
}
static <T> T method(T a, T b) {
   return null;
}

このmethod(a, b)の型推論で、Tはどのような型に算出されるかという問題。
15.12.2.8 Inferring Unresolved Type Argumentsを追いかけながら進んでいく感じ。ただし、型推論の後半部分だけ。

おさらいしておくと、Integer, Long はそれぞれ下記のような宣言。

class Integer extends Number implements Comparable<Integer>
class Long extends Number implements Comparable<Long>
class Number extends Object implements Serializable

おおざっぱに省略すると、T :> Integer, T :> Long という制約になる。なので、Tの型は{Integer, Long}の最小限の上限境界型と一致するはず。これは、Integer, Longの共通のスーパータイプであり、さらにそれらの中で最も限定的なもののこと。
これは最終的に、least upper bound (lub: 最小限の上限境界)が求められればいい。{Integer, Long}の最小の上限境界型をlub({Integer, Long}), この結果をRとおく。

Java言語仕様第三版(和訳)の406ページあたりから追いかけてみる。

それぞれの型の super type set (ST: スーパータイプの集合)を算出する。これはInteger, Longのextendsとimplementsを追いかけて行って出現するものを列挙するだけ。

ST(Integer) = {
    Integer, Number, Object, Comparable<Integer>, Serializable
}
ST(Long) = {
    Long, Number, Object, Comparable<Long>, Serializable
}

STにイレイジャ変換を適用して、erased super type set (EST: 型を消去したスーパータイプの集合)を算出する。STから<>を取り除いただけ。

EST(Integer) = {
    Integer, Number, Object, Comparable, Serializable
}
EST(Long) = {
    Long, Number, Object, Comparable, Serializable
}

ESTの共通部分集合をとって、erased candidate set (EC: 型を消去した候補の集合)を算出する。Integer, Longがそれぞれ消える。

EC({Integer, Long}) = EST(Integer) & EST(Long) = {
    Number, Object, Comparable, Serializable
}

ECのうち他の型のスーパータイプになるものを除去して、minimal erased candidate set (MEC: 型を消去した候補の最小限の集合)を算出する。Object :> Number, Serializable :> Number なのでそれぞれ消える。

MEC({Integer, Long}) = {
    Number, Comparable
}

MECのそれぞれの型について型引数を復元し、invocation (Inv: 型を復元した候補の最小限の集合*1 )を算出する。これは ST → EST の逆をとればいいだけ。

Inv(Number, {Integer, Long}) = {
    Number // = { Number, Number }
}
Inv(Comparable, {Integer, Long}) = {
    Comparable<Integer>, Comparable<Long>
}

Invの要素が持つ型引数から、それらを包括する最小限の型、least containing invocation (lci: 型を復元した候補を包括する最小限の型*2 *3 )を算出する。Numberは復元するまでもないのでそのまま利用、Comparableが{Integer, Long}で異なるので後述のlctaを使って算出する。

lci({Number}) = Number
lci({Comparable<Integer>, Comparable<Long>})
    = Comparable<lcta({Integer, Long})>

lciの途中に出てきたleast containing type argument(lcta: 型引数の最小の上限境界*4 )を算出する。定義「lcta({U, V}) = ? extends lub({U, V}), where U != V」を利用する。

lcta({Integer, Long})
    = ? extends lub({Integer, Long})

と、ここで最初に求めたかったlub({Integer, Long})が再度出現する。これはとりあえず最初に定義したRとおいておく。

これで、先ほどのlciが計算できる。

lci({Comparable<Integer>, Comparable<Long>})
    = Comparable<lcta({Integer, Long})>
    = Comparable<? extends lub({Integer, Long})>
    = Comparable<? extends R>

それぞれのlciの共通型(intersection type: 4.9 Intersection Types)が、最終的なlub({Integer, Long})の結果となる。途中でglb(greatest lower bound, 最大限の下限境界)の計算があるけど、これは共通型を計算すれば終わりなので割愛。

lub({Integer, Long})
    = glb({ lci({Number}), lci({Comparable<Integer>, Comparable<Long>}) })
    = glb({ Number, Comparable<? extends R> })
    = Number & Comparable<? extends R>

つまり、

lub({Integer, Number}) as R = Number & Comparable<? extends R>

となる。

これはRを含む無限型なので、表示できない。

Number & Comparable<? extends
  Number & Comparable<? extends
    Number & Comparable<? extends
      Number & Comparable<? extends
        Number & Comparable<? extends
          Number & Comparable<? extends Comparable<...>>>>>>>

最初に戻ると、下記のresultはどうなるかということだった。

public static void main(String...args) {
   Integer a = 1;
   Long b = 2;
   Object result = method(a, b); // resultの型は?
}
static <T> T method(T a, T b) {
   return null;
}

これのmethodの総称宣言に、先ほどの型を上限境界として加えてやる。

public static void main(String...args) {
   Integer a = 1;
   Long b = 2;
   Object result = method(a, b);
}
// 上限境界を設定
static <T extends Number & Comparable<? extends T>> T method(T a, T b) {
   return null;
}

しかしなんかうまくいかない(見やすいように加工済み)。

..: method(T,T) を (Integer,Long) に適用できません
        Object result = method(a, b);
                        ^
エラー 1 個

なんだろう…。どこかに見落としがあったらツッコミお願いします。

ちなみに、上記はSunのjavacでやった結果です。EclipseのJDTでやるともっと精度が悪いので、後ほど言及します。

追記

一つ上にjavacとJDTのエントリを書いた。

*1:言語仕様では「起動」と訳していた

*2:言語仕様では「最小限の包括起動」と訳していた

*3:包括については合ってたらしい - しげるメモとかに

*4:言語仕様では「最小限の包括型引数」と訳していた