可変長引数ではAuto Unboxingが優先される?の理由

可変長引数ではAuto Unboxingが優先される? - kaisehのブログへのツッコミ。米欄に書こうとしたら長すぎたのでこちらに。

ところが可変長引数の場合は、評価の優先順位が違うみたいです。

次のようにプログラムに書き足すと、

public class Test {
    static void foo(int arg)        { System.out.println("foo: int"); }
    static void foo(Object arg)     { System.out.println("foo: Object"); }

    static void bar(int... args)    { System.out.println("bar: int..."); }
    static void bar(Object... args) { System.out.println("bar: Object..."); }

    public static void main(String[] args) {
        Integer n = 1;
        foo(n);
        bar(n);
    }
}

結果はこう。

foo: Object
bar: int...
可変長引数ではAuto Unboxingが優先される? - kaisehのブログ

ポイントは2点です。

  1. 〜Java1.4との下位互換性のため、メソッド呼び出しは3段階に分けて検索される
  2. オーバーロードよりサブタイプな引数を持つメソッドほど優先される

ただ、言語仕様を読む限りだと実はこのケースが漏れてるんじゃ…という疑惑が。一部拡大解釈してます。

3段階に分けて検索される

...
A method is applicable if it is either applicable by subtyping (§15.12.2.2), applicable by method invocation conversion (§15.12.2.3), or it is an applicable variable arity method (§15.12.2.4).

15.12.2 Compile-Time Step 2: Determine Method Signature

あんまり厳密じゃない言い方をすれば、メソッドは次の順序で検索されます。

  1. by subtyping: Java1.4までの探し方
  2. by method invocation conversion: ↑+オートボクシング
  3. + VARARGS: ↑+可変長引数

そして、上から順番に探していって、ひとつでも見つかったら検索を打ち切ります

このため、foo, bar はつぎのように絞られます

  • foo -> foo(Object) [by subtyping]
  • bar -> bar(int...) or bar(Object...) [VARARGS]

foo(n)の呼び出しは、foo(Object)が見つかった瞬間に検索を打ち切られます(foo(int)は候補にも挙がりません)。これがまず、「固定長引数ではオートボクシングが優先されない」理由です。

対して、bar(n)の呼び出しは3段階目(VARARGS)まで検索して、bar(int...), bar(Object...) の両方が候補に残ってしまいます。

両方が候補に残った場合、2つ目のポイントよりサブタイプな引数を持つメソッドほど優先されるで考えることになります。

よりサブタイプな引数を持つメソッドほど優先される

If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.

15.12.2.5 Choosing the Most Specific Method

foo(String)とfoo(Object)という二つのメソッドがあったとき、foo("AAA")と呼び出すとfoo(String)が実際に実行されるってのは経験上OKかと思います。これはStringがObjectのサブタイプで、この場合にfoo(String)はfoo(Object)にくらべてmore specificであるといいます。
オーバーロードで呼び出し可能なメソッドが複数候補に残っている場合、引数の型を比較してmost specificなメソッドを探すという処理が行われます。

本当はここでは比較にジェネリック型の起動とサブタイピング(4.10 Subtyping)しか使ってはいけないはずなのですが、(アン)ボクシング変換を使っているところをみると、どこか私が見落としている点があるのかもしれません。

ともあれ、代入変換(オートボクシング可なサブタイピング)をOKとすると、

int i = ...;
Object o = ...;
o = i;

が成立するので、int << Object であることから bar(int...) が優先されたのではないかと。むしろ、foo(Object)が優先されるのが下位互換性を考えた例外と捉えることもできるかもしれません。

解答としてはいまいちなので、誰か補足または正解求む。