25

ケース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 の出力: hello10

ケース 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。しかし、私には理解できませんでした。なんで ?call()しかし、からのメソッドのいずれかをコメントアウトすると、Case 2正常に機能します。ここで何が起こっているのか、誰かが理解するのを手伝ってくれますか?

4

6 に答える 6

15

最も具体的なメソッドの検索は、Java 言語仕様 (JLS) で非常に正式な方法で定義されています。なるべく形式的な公式を省きながら、主に該当するものを以下に抜粋しました。

要約すると、質問に当てはまる主な項目は次のとおりです。

  • JLS 15.12.2 : ユースケースはフェーズ 3 に該当します。

第 3 フェーズ (§15.12.2.4) では、オーバーロードを可変アリティ メソッド、ボックス化、およびボックス化解除と組み合わせることができます。

  • 次に、JLS 15.12.2.4Integer...は基本的に両方の方法が適用可能であると判断します。これは、 10 を anまたは an の両方に変換できるためint...です。ここまでは順調ですね。そしてパラグラフは次のように結論付けています。

最も具体的なメソッド (§15.12.2.5) が、適用可能な可変アリティ メソッドから選択されます。

  • これにより、JLS 15.12.2.5が表示されます。この段落では、アリティ メソッドm(a...)が別のアリティ メソッドよりも具体的である条件を示しm(b...)ます。1 つのパラメーターを使用し、ジェネリックを使用しないユース ケースでは、要約すると次のようになります。

m(a...)m(b...)はiifよりも具体的です。a <: bここで、 は<:を意味しis a subtype ofます。

が のサブタイプではintなく、 のサブタイプではないことが起こります。IntegerIntegerint

したがって、JLS 言語を使用するには、どちらのcall方法も最大限に具体的です (どちらの方法よりも具体的な方法はありません)。この場合、同じ段落は次のように結論付けています。

  • すべての最大限に固有のメソッドにオーバーライドと同等の (§8.4.2) 署名がある場合 [...] => ジェネリックが含まれておらず、Integer と int が異なるパラメーターであるため、あなたのケースではありません
  • それ以外の場合、メソッド呼び出しがあいまいであると言い、コンパイル時エラーが発生します。

ノート

たとえば、次のように置き換えInteger...た場合、最も具体的な方法は* になります。 同様に、 に置き換えた場合、メソッドが最も具体的になります。long...int <: longcall(int...)
int...Number...call(Integer...)

*実際、Java 7 より前の JDK には、その状況であいまいな呼び出しを示すバグがありました。

于 2013-01-02T14:56:42.427 に答える
4

OpenJDK 7で修正されたように見えるバグ#6886431に関連しているようです。

以下はバグの説明です。

バグの説明:

次のオーバーロードされたシグニチャを使用してメソッドを呼び出すと、あいまいなエラーが発生することが予想されます(引数が両方と互換性があると想定)。

int f(Object... args);
int f(int... args);

javacは、2番目を最初よりも具体的なものとして扱います。この動作は賢明ですが(私はそれを好みます)、JLS(15.12.2)と矛盾しています。

于 2012-12-27T11:17:01.980 に答える
2

JLS 15.12.2.2から

JLS 15.12.2.2 最も具体的な方法を選択する

2 つ以上のメソッド宣言がアクセス可能であり、メソッド呼び出しに適用できる場合、ランタイム メソッド ディスパッチの記述子を提供するために 1 つを選択する必要があります。Java プログラミング言語では、最も具体的な方法が選択されるという規則が使用されます。非公式の直観は、最初のメソッドによって処理された呼び出しが、コンパイル時の型エラーなしで別のメソッドに渡される場合、あるメソッド宣言が別のメソッド宣言よりも具体的であるということです。

これらのメソッドはどちらも他のメソッドに渡すことができません (int[] と Integer[] の型は関連していません) したがって、呼び出しはあいまいです

于 2012-12-27T11:40:55.787 に答える
1

コンパイラは、どのメソッドを呼び出す必要があるかを知りません。これを修正するには、入力パラメータをキャストする必要があります..

public static void main(String... args) {
  call((int)10);
  call(new Integer(10));
}

編集:

これは、コンパイラが Integer を int に変換しようとするためです。したがって、callメソッドの呼び出しの前に暗黙のキャストが行われます。そのため、コンパイラは int を取ることができるその名前のメソッドを探します。そして、あなたはそれらのうちの2つを持っているので、コンパイラは両方のどちらを呼び出すべきかを知りません.

于 2012-12-27T11:07:09.343 に答える
0

この質問はすでに何度も聞かれています。トリッキーな部分は、f(1, 2, 3)明らかに 's を渡しているのに、コンパイラがバージョンintを選択できないのはなぜですか? 答えは、私が頭を悩ませているJLSf(int...)のどこかにあるはずです

§15.12.2.4 によると、両方のメソッドが適用可能な可変アリティ メソッドであるため、次のステップは最も具体的なものを特定することです。

残念ながら、§15.12.2.5ではサブタイプ テストT i <: S if1(T 1 , .. T n )f2(S 1 , .. S n )の仮パラメーターの間に使用してターゲット メソッドを識別します。 int :> IntegerでもInteger :> intでもないため、と の間にサブタイプの関係はIntegerありません。段落の最後に次のように述べられています。int

上記の条件は、ある方法が別の方法よりも具体的である可能性がある唯一の状況です。[...]

m1 がm2 よりも具体的で、m2 が m1 より具体的でない場合にのみ、メソッド m1 は別のメソッド m2 より厳密に具体的です。

メソッドがアクセス可能で適用可能であり、適用可能でアクセス可能で厳密により具体的なメソッドが他にない場合、そのメソッドはメソッド呼び出しに対して最大限に固有であると言われます。

最も具体的なメソッドが 2 つ以上あるため、最も具体的なメソッドがない可能性があります。この場合:

  1. [...]

  2. それ以外の場合、メソッド呼び出しがあいまいであると言い、コンパイル時エラーが発生します。

Gilad Brachaによるブログ投稿(展示 2 を参照) を添付し、@Jayamhona の回答からのバグ レポートにリンクします。

于 2012-12-27T12:06:10.500 に答える
0

複数のメソッドを適用できる場合は、Java 言語仕様の「最も具体的なメソッドの選択」のパラグラフよりも次のようになり15.12.2.5ます。

次のいずれかの場合、指定された1 つmの可変アリティ メンバー メソッドは、同じ名前の別の可変アリティ メンバー メソッドよりも具体的です ( <: means subtyping):

  1. 1 つのメンバー メソッドには n 個のパラメーターがあり、もう 1 つのメンバー メソッドには k 個のパラメーターがあります。ここで、n ≥ k であり、次のとおりです。
    • 最初のメンバ メソッドのパラメータの型は、T1、...、Tn-1、Tn[] です。( Integer[], n=1 である T_n[] は 1 つしかありません)
    • もう一方のメソッドのパラメーターの型は、U1、...、Uk-1、Uk[] です。(ここでも、int[], k=1 である 1 つのパラメーターのみ)
    • 2 番目のメソッドがジェネリックの場合、R1 ... Rp (p ≥ 1) をその型パラメーターとし、Bl を Rl (1 ≤ l ≤ p) の宣言された境界とし、A1 ... Ap を推論される型引数とします。 (§15.12.2.7) この呼び出しについて、初期制約 Ti << Ui (1 ≤ i ≤ k-1) および Ti << Uk (k ≤ i ≤ n) の下で、Si = Ui[R1=A1,. ..,Rp=Ap] (1 ≤ i ≤ k). (メソッドはジェネリックではありません)
    • それ以外の場合は、Si = Ui (1 ≤ i ≤ k) とします。( S1 = int[] )
    • 1 から k-1 までのすべての j に対して、Tj <: Sj、および (ここには何もありません)
    • k から n までのすべての j について、Tj <: Sk、および ( T1 <: S1, Integer[] <: int[] を比較)
    • 2 番目の方法が上記の一般的な方法である場合、Al <: Bl[R1=A1,...,Rp=Ap] (1 ≤ l ≤ p) となります。(メソッドはジェネリックではありません)

プリミティブintは wrapperIntegerにオートボックス化されていますが、最初の条件が成り立たないよりも にint[]オートボックス化されていません。Integer[]

2番目の条件はほぼ同じです。

保持されない他の条件もあり、JLS が原因です。

メソッドの呼び出しがあいまいで、コンパイル時にエラーが発生すると言います。

于 2012-12-27T11:50:07.393 に答える