ケース1
_static void call(Integer i) {
System.out.println("hi" + i);
}
static void call(int i) {
System.out.println("hello" + i);
}
public static void main(String... args) {
call(10);
}
_
ケース1の出力:hello1
ケース2
_static void call(Integer... i) {
System.out.println("hi" + i);
}
static void call(int... i) {
System.out.println("hello" + i);
}
public static void main(String... args) {
call(10);
}
_
コンパイルエラー_reference to call ambiguous
_を表示します。しかし、私には理解できませんでした。どうして ?しかし、_Case 2
_のcall()
メソッドのいずれかをコメントアウトすると、正常に動作します。誰かが私が理解するのを助けることができますか、ここで何が起こっているのですか?
最も具体的な方法を見つけることは、Java Language Specificaion(JLS)で非常に形式的に定義されています。形式式をできるだけ削除しようとするときに適用される主な項目を以下に抽出しました。
要約すると、質問に適用される主な項目は次のとおりです。
3番目のフェーズ(§15.12.2.4)では、オーバーロードを可変アリティメソッド、ボックス化、およびボックス化解除と組み合わせることができます。
Integer...
_または_int...
_の両方に変換できるため、基本的に両方の方法が適用可能であると判断します。ここまでは順調ですね。そして、段落は結論付けます:最も具体的なメソッド(§15.12.2.5)は、適用可能な可変アリティメソッドの中から選択されます。
m(a...)
が別のアリティメソッドm(b...)
よりも具体的である条件を示します。パラメータが1つあり、ジェネリックスがないユースケースでは、具体的には次のようになります。
m(a...)
はm(b...)
iif _a <: b
_よりも具体的です。ここで、_<:
_は_is a subtype of
_を意味します。
int
はInteger
のサブタイプではなく、Integer
はint
のサブタイプではない場合があります。
したがって、JLS言語を使用するには、両方のcall
メソッドが最大限に特定されます(他の方法よりも特定のメソッドはありません)。この場合、同じ段落で次のように結論付けています。
- 最大限に特定されたすべてのメソッドにオーバーライド等価(§8.4.2)のシグネチャがある場合[...]=>ジェネリックが含まれておらず、Integerとintは異なるパラメーターであるため
- それ以外の場合は、メソッドの呼び出しがあいまいであり、コンパイル時エラーが発生します。
[〜#〜]ノート[〜#〜]
たとえば、_Integer...
_を_long...
_に置き換えた場合、_int <: long
_となり、最も具体的なメソッドはcall(int...)
*になります。
同様に、_int...
_を_Number...
_に置き換えた場合、call(Integer...)
メソッドが最も具体的になります。
*実際には Java 7より前のJDKにはバグがあり、この状況ではあいまいな呼び出しを示す がありました。
bug#6886431 に関連しているようですが、OpenJDK 7で修正されているようです。
以下はバグの説明です。
バグの説明:
次のオーバーロードされたシグネチャを使用してメソッドを呼び出すと、曖昧なエラーが発生します(引数が両方と互換性があると想定)。
int f(Object... args);
int f(int... args);
javacは、2番目のものを最初のものよりも具体的なものとして扱います。この動作は賢明です(私はそれを好みます)が、JLS(15.12.2)と一貫性がありません。
JLS 15.12.2.2最も具体的な方法を選択する
2つ以上のメソッド宣言がアクセス可能であり、メソッド呼び出しに適用できる場合、実行時メソッドディスパッチの記述子を提供するために1つを選択する必要があります。 Javaプログラミング言語は、最も具体的なメソッドが選択されるという規則を使用します。非公式な直感は、最初のメソッドによって処理された呼び出しがもう1つはコンパイル時のタイプエラーなしです。
これらのメソッドはどちらも他に渡すことができません(int []およびInteger []の型は関連していません)。したがって、呼び出しはあいまいです。
コンパイラーは、どのメソッドを呼び出す必要があるかを認識していません。これを修正するには、入力パラメーターをキャストする必要があります。
public static void main(String... args) {
call((int)10);
call(new Integer(10));
}
編集:
これは、コンパイラがIntegerをintに変換しようとするためです。したがって、call
メソッドを呼び出す前に、暗黙のキャストが行われます。したがって、コンパイラーは、intを取ることができるその名前のメソッドを探します。そして、あなたはそれらの2つを持っているので、コンパイラーはどちらが呼び出されるべきかを知りません。
この質問 既に質問されています 何度も。トリッキーな部分は、f(1, 2, 3)
が明らかにint
を渡しているため、コンパイラがf(int...)
バージョンを選択できないのはなぜですか?答えは [〜#〜] jls [〜#〜] のどこかにある必要があります。
§15.12.2.4によると、どちらの方法も該当する可変アリティ法なので、次のステップは最も具体的な方法を識別することです。
残念ながら、 §15.12.2.5 はサブタイプテストTを使用します私 <:S私f1(T1、.. Tん)およびf2(S1、.. Sん)ターゲットメソッドを識別するための仮パラメーター、およびInteger
とint
の間にサブタイプの関係がないため、誰もwins、なぜならint:> IntegerでもInteger:> intでもないからです。段落の終わりに述べられています:
上記の条件は、ある方法が別の方法よりも具体的である唯一の状況です。 [...]
メソッドm1は、m1がm2よりも限定的であり、m2がm1よりも限定的でない場合に限り、別のメソッドm2よりも厳密により具体的です。
メソッドがアクセス可能で適用可能であり、適用可能でアクセス可能なメソッドが厳密に存在しない場合、メソッド呼び出しに対してメソッドは最大限に特定されますより具体的。
最も限定的な方法が2つ以上あるため、最も限定的な方法がない可能性があります。この場合:
[...]
それ以外の場合は、メソッドの呼び出しがあいまいであり、コンパイル時エラーが発生します。
添付 ブログ投稿 Gilad Bracha(展示2を参照)、@ Jayamhonaの回答からのバグレポートにリンク。
複数のメソッドを適用できる場合は、からJava言語仕様私たち 最も具体的なメソッドの選択 から、段落15.12.2.5
:
m
という名前の1つの変数アリティメンバーメソッドは、同じ名前の別の変数アリティメンバーメソッドよりも具体的です(<: means subtyping
)。
プリミティブint
はラッパーInteger
にオートボックス化されますが、int[]
はInteger[]
にオートボックス化されません。最初の条件が満たされないためです。
2番目の条件はほとんど同じです。
保持されない他の条件もありますが、JLSが原因です。
メソッドの呼び出しがあいまいであり、コンパイル時エラーが発生するとします。