他の回答のほとんどに同意しますが、まだ回答されていない部分が 1 つあります。コンパイラの違いはなぜですか?
よく見ると、これは ecj と javac の違いではなく、JLS の異なるバージョン間の違いです。
- コンプライアンス 1.7 以下でコンパイルすると、両方のコンパイラが最初の (単一の引数) メソッドを選択します。
- コンプライアンス 1.8 でコンパイルすると、両方のコンパイラが 2 番目の (varargs) メソッドを選択します。
生成されたバイトコードを見てみましょう。
7: invokestatic #8 // Method anyObject:()Ljava/lang/Object;
10: checkcast #9 // class "[Ljava/lang/Object;"
13: invokevirtual #10 // Method doThis:([Ljava/lang/Object;)V
(両方のコンパイラもこれらのバイトに同意します)
つまり、Java 8 の推論はより強力になり、 to の型引数を推論できるようになりましanyObject()
たObject[]
。checkcast
これは、ソース コードに変換された次の命令で確認できます(Object[])anyObject()
。これは、 もdoThis(Object...)
varargs マジックなしで呼び出すことができることを意味しますが、 type の引数を 1 つ渡すだけObject[]
です。
これで、両方のメソッドが同じカテゴリ (「固定アリティ呼び出しによって適用可能」) で適用可能になり、適用可能なメソッドの中で最も具体的なものを検索すると、2 番目のメソッドが選択されます。
対照的に、Java 7 では 2 番目のメソッドの呼び出しは可変アリティ呼び出しとしてのみ許可されますが、固定アリティの一致が見つかった場合でも、これは試行されません。
上記は、JLS の変更に対してプログラムを堅牢にする方法も示しています。
verify(mock, times(1)).doThis(Matchers.<Object>anyObject());
これにより、すべてのバージョンのすべてのコンパイラに最初のメソッドを選択するように指示されます。これは、常にdoThis()
asの引数が表示されるObject
ためです。この不健康なオーバーロードを本当に回避できない場合、つまり :)