web-dev-qa-db-ja.com

コンパイラエラー:あいまいな呼び出しへの参照

ケース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()メソッドのいずれかをコメントアウトすると、正常に動作します。誰かが私が理解するのを助けることができますか、ここで何が起こっているのですか?

24
Ravi

最も具体的な方法を見つけることは、Java Language Specificaion(JLS)で非常に形式的に定義されています。形式式をできるだけ削除しようとするときに適用される主な項目を以下に抽出しました。

要約すると、質問に適用される主な項目は次のとおりです。

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

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

  • 次に JLS 15.12.2.4 は、10を_Integer..._または_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_を意味します。

intIntegerのサブタイプではなく、Integerintのサブタイプではない場合があります。

したがって、JLS言語を使用するには、両方のcallメソッドが最大限に特定されます(他の方法よりも特定のメソッドはありません)。この場合、同じ段落で次のように結論付けています。

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

[〜#〜]ノート[〜#〜]

たとえば、_Integer..._を_long..._に置き換えた場合、_int <: long_となり、最も具体的なメソッドはcall(int...) *になります。
同様に、_int..._を_Number..._に置き換えた場合、call(Integer...)メソッドが最も具体的になります。

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

14
assylias

bug#6886431 に関連しているようですが、OpenJDK 7で修正されているようです。

以下はバグの説明です。

バグの説明:

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

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

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

4
Jayamohan

から JLS 15.12.2.2

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

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

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

2
radai

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

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

編集:

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

1
aphex

この質問 既に質問されています 何度も。トリッキーな部分は、f(1, 2, 3)が明らかにintを渡しているため、コンパイラがf(int...)バージョンを選択できないのはなぜですか?答えは [〜#〜] jls [〜#〜] のどこかにある必要があります。

§15.12.2.4によると、どちらの方法も該当する可変アリティ法なので、次のステップは最も具体的な方法を識別することです。

残念ながら、 §15.12.2.5 はサブタイプテストTを使用します <:Sf1(T1、.. Tおよびf2(S1、.. Sターゲットメソッドを識別するための仮パラメーター、およびIntegerintの間にサブタイプの関係がないため、誰もwins、なぜならint:> IntegerでもInteger:> intでもないからです。段落の終わりに述べられています:

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

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

メソッドがアクセス可能で適用可能であり、適用可能でアクセス可能なメソッドが厳密に存在しない場合、メソッド呼び出しに対してメソッドは最大限に特定されますより具体的。

最も限定的な方法が2つ以上あるため、最も限定的な方法がない可能性があります。この場合:

  1. [...]

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

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

0
Raffaele

複数のメソッドを適用できる場合は、からJava言語仕様私たち 最も具体的なメソッドの選択 から、段落15.12.2.5

mという名前の1つの変数アリティメンバーメソッドは、同じ名前の別の変数アリティメンバーメソッドよりも具体的です(<: means subtyping)。

  1. 1つのメンバーメソッドにはn個のパラメーターがあり、もう1つにはk個のパラメーターがあります。ここで、n≥kであり、
    • 最初のメンバーメソッドのパラメーターの型は、T1、...、Tn-1、Tn []です。 (T_n []はInteger []、n = 1の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、および(Compare T1 <:S1、Integer [] <:int []
    • 2番目の方法が上記の一般的な方法である場合、Al <:Bl [R1 = A1、...、Rp = Ap](1≤l≤p)。 (メソッドはジェネリックではありません

プリミティブintはラッパーIntegerにオートボックス化されますが、int[]Integer[]にオートボックス化されません。最初の条件が満たされないためです。

2番目の条件はほとんど同じです。

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

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

0
mishadoff