web-dev-qa-db-ja.com

可変引数Javaあいまいな呼び出し

私はJavaのvarargsメソッドについて少し混乱しています:

_public static int sum(int ...a) {
    return 0;
}

public static double sum(double ...a) {
    return 0.0;
}
_

引数を渡さずにsum()を呼び出そうとすると、intバージョンのメソッドが呼び出されました。理由がわかりません。通常、コンパイラはエラーを発生させる必要があります。

対照的に、次のコードは、引数なしでsumを呼び出そうとすると、コンパイラエラーを生成します。

_public static int sum(int ...a) {
    return 0;
}

public static boolean sum(boolean ...a) {
    return true;
}
_
69
karim

ここで適用される一般的なルールは次のとおりです。一方のメソッドシグネチャが他方よりも厳密により具体的である場合、Javaエラーなしでそれを選択します。

直感的には、メソッドシグネチャは完全に削除できればより具体的であり、もう一方のより具体的でないものは既存の各呼び出しに適用できます。

シグニチャsum(int... args)sum(double... args)のどちらかを選択すると、シグニチャsum(int... args)はより具体的になります。これは、そのメソッドの呼び出しを適用することでsum(double... args)に渡すこともできるためです。コンバージョンの拡大。同様に変換できないsum(boolean... args)メソッドについても同じことが当てはまりません。

Java言語仕様、SE 8バージョン:

15.12。メソッド呼び出し式

15.12.2.5。最も具体的な方法の選択

Javaプログラミング言語は、最も具体的なメソッドが選択されるという規則を使用します。

.。

次のいずれかに該当する場合、引数式e1、...、ekを使用した呼び出しに対して、1つの適用可能なメソッドm1は別の適用可能なメソッドm2よりも具体的です。

.。

  • m2はジェネリックではなく、m1とm2は厳密または緩い呼び出しによって適用可能であり、m1が仮パラメータータイプS1、...、Snを持ち、m2が仮パラメータータイプT1、...、Tnを持っている場合、タイプSiはすべてのi(1≤i≤n、n = k)の引数eiについて、Tiよりも具体的

.。

S <:T(§4.10)の場合、どの式に対してもタイプSはタイプTよりも具体的です。


4.10。サブタイピング

4.10.1。プリミティブ型間のサブタイピング

ダブル> 1フロート

フロート> 1ロング

long> 1 int

56
Marko Topolnik

この回答 で述べたように、使用するオーバーロードされたメソッドを選択するときに従うルールがあります。

引用するには:

  1. プリミティブの拡張では、可能な限り最小のメソッド引数を使用します
  2. ラッパータイプを別のラッパータイプに拡張することはできません
  3. IntからIntegerにボックス化し、Objectに拡張できますが、Longには拡張できません
  4. 拡大はボクシングを打ち負かし、ボクシングはVar-argsを打ち負かします。
  5. ボックス化してから拡大することができます(intは整数を介してオブジェクトになることができます)
  6. 広げてからボックス化することはできません(intを長くすることはできません)
  7. Var-argsをboth widening and boxingと組み合わせることはできません。

(ルール1を次のように再定義しましょう:「プリミティブの拡大では、可能な限り最も具体的なメソッド引数を使用します。」)

したがって、これらのルールを念頭に置いて、ここで何が起こっているのかを知ることができます。

ルール1によると、プリミティブの拡張では、可能な限り最も具体的なメソッド引数が使用されます。 intは非10進数(例:1)で表され、doublefloat(例:1.0)よりも32バイト高い精度の10進数で表されるため、次のことができます。 intsはdoublesより「小さい」または「小さい」と言います。そのロジックにより、intsはdoublesに「昇格」でき、doublesはintsに「降格」できます。

簡単に言えば、別のプリミティブに拡張できるプリミティブ(たとえば、int-> float-> double)は、他のプリミティブよりもより具体的です。たとえば、11.0にプロモートできるため、intdoubleよりもより具体的です。

同じ名前のこれらのオーバーロードされたvarargメソッドに引数を渡さなかった場合、戻り値は事実上同じであるため(それぞれ、0と0.0)、コンパイラーは、タイプintのvarargを受け取るメソッドを使用することを選択します。 より具体的に

したがって、それぞれintsとbooleans(互いに拡張できないタイプ)を取り込むこれらの同じメソッドを導入すると、intsはintsのように「プロモート」または「デモート」できないため、コンパイラは使用するメソッドを選択できなくなります。 、floatsおよびdoubles。したがって、コンパイルエラーがスローされます。

これが何が起こっているのかを理解するのに役立つことを願っています。

7
hmc_jake