パラメータでのJavaタイプの昇格


20

私はこのスニペットを偶然見つけました:

public class ParamTest {
    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

これにより、コンパイルエラーが発生します。

エラー:(15、9)java:PrintSumへの参照は、ParamTestのメソッドprintSum(int、double)とParamTestのメソッドのprintSum(long、long)の両方であいまいです

これはどのようにあいまいですか?この場合、最初のパラメーターは既にintであるため、2番目のパラメーターだけを昇格する必要はありませんか?この場合、最初のパラメーターを昇格する必要はありませんか?

コードを更新して別のメソッドを追加すると、コンパイルは成功します。

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

明確にするために拡大させてください。以下のコードはあいまいさをもたらします:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

次に、以下のこのコードあいまいになります。

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

ただし、これはあいまいさを引き起こしません

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, double b) {
        System.out.println("In longDBL " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

2
コンパイラーは呼び出しprintSum(1、2);をマップできます。printSum(long a、long b)またはprintSum(int a、double b)のいずれかであるため、あいまいです。次のようにタイプを正確に指定することにより、コンパイラーが明示的に選択できるようにする必要があります。printSum(1、2d)
Ravindra Ranwala

6
エラーメッセージを誤って引用したため、違いは非常に重要です。実際のエラーメッセージは次のとおりError:(15, 9) java: reference to printSum is ambiguous both method printSum(int,double) in ParamTest and method printSum(long,long) in ParamTest matchです。-あいまいなのはメソッドではなく、あいまいなのはメソッドの呼び出しです。
Erwin Bolwidt

1
@ErwinBolwidtエラーメッセージはeclipseからのものでした。とにかく、printSum(int a、long b)を追加するとエラーメッセージが削除されるため、私はまだそれを理解していません。
riruzen

2
JLS-5.3 => 緩やかな呼び出しコンテキストで許可されている変換では、式の型をパラメーターの型に変換できない場合、コンパイル時エラーが発生します。コンテキストに適用できるようですが、方法を推測するのは本当に簡単ではありません。+1
ナマン

1
私たちは間違いなく、このための標準的な質問が必要です:stackoverflow.com/…。」
Marco13

回答:


17

これは、JLSの15.12.2.5に関する特定のルールと関係があると思います最も具体的な方法の選択。それはそれを述べています:

複数のメンバーメソッドがアクセス可能であり、メソッド呼び出しに適用できる場合は、実行時メソッドディスパッチの記述子を提供するために1つを選択する必要があります。Javaプログラミング言語は、最も具体的なメソッドが選択されるという規則を使用します。

Javaが最も具体的な方法を選択する方法は、次のテキストでさらに説明されています。

非公式な直感は、最初のメソッドによって処理された呼び出しをコンパイル時エラーなしに他のメソッドに渡すことができれば、1つのメソッドが別のメソッドよりも具体的であるということです。明示的に型指定されたラムダ式引数(§15.27.1)または可変アリティ呼び出し(§15.12.2.4)などの場合、1つの署名を他の署名に適応させるためにある程度の柔軟性が許可されます。

あなたの例の場合、すべてのメソッドはアクセス可能であり、メソッド呼び出しに適用できるため、Javaはどのメソッドが最も固有であるかを判別する必要があります。

これらの方法の場合、より具体的であると判断できるものはありません。

public static void printSum(int a, double b) {
    System.out.println("In intDBL " + (a + b));
} // int, double cannot be passed to long, long or double, long without error

public static void printSum(long a, long b) {
    System.out.println("In long " + (a + b));
} // long , long cannot be passed to int, double or double, long without error

public static void printSum(double a, long b) {
    System.out.println("In doubleLONG " + (a + b));
} // double, long cannot be passed to int, double or long, long without error

4番目の方法は、最も具体的になるために必要な条件を満たすため、曖昧さを解消します。

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

つまり、(int、long)は、コンパイルエラーなしで(int、double)、(long、long)、または(double、long)に渡されます。


2
要約すると、すべてのメソッドが呼び出しでアクセスできる場合、javaはメソッドを識別します。これは、コンパイル時エラーなしで他のメソッドを呼び出すことができます。見つかった場合は、それを最も具体的なものとして使用し、それ以外の場合はあいまいエラーを呼び出しますか?これは@riruzenの正しい答えだと思います。
Sandeep Kokate、

ありがとう!私はこれを(double、int)対(double、long)で試してみましたが、あいまいさが明確になり、(double、int)対(long、double)は期待どおりにあいまいでした。
riruzen

7

それは確かに非常に興味深い質問です。Java言語仕様を1つずつ見ていきましょう。

  1. コンパイラーが適用可能な可能性のあるメソッドを識別しようとする場合、コンパイラーが最初に行うことは、Strict Invocationによって適用可能なメソッドを検索することです。

  2. あなたの場合、そのようなメソッドはないので、次のステップは、Loose Invocationによって適用可能なメソッド見つけることです

  3. この時点ですべてのメソッドが一致するため、ルーズ呼び出しで適用できるメソッドの中から最も具体的なメソッド(§15.12.2.5)が選択されます。

これは重要な瞬間なので、これを詳しく見てみましょう。

次のいずれかに該当する場合、引数式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よりも具体的です。

S <: Tは、それSがのサブタイプであることを意味しTます。プリミティブの場合、次の関係があります。

double > float > long > int

それでは、メソッドを見て、どのメソッドが他よりも具体的であるかを見てみましょう。

public static void printSum(int a, double b) {  // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(double a, long b) { // method 2
    System.out.println("In doubleLONG " + (a + b));
}

この例では、メソッド1の最初のパラメーターは、メソッド2の最初のパラメーターよりも明確です(整数値で呼び出す場合:)printSum(1, 2)しかし、2番目のパラメータは、方法2のために、より具体的であるため、long < double。したがって、これらの方法はどれも、他より具体的ではありません。そのため、ここではあいまいさが発生します。

次の例では:

public static void printSum(int a, double b) { // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(long a, double b) { // method 2
    System.out.println("In longDBL " + (a + b));
}

メソッド1の最初のパラメータータイプはメソッド2のパラメータータイプよりも具体的ですint < long。2番目のパラメータータイプは両方とも同じであるため、メソッド1が選択されています。


私はあなたの回答に反対票を投じませんでしたが、この回答は、(int、double)が(long、long)とあいまいである理由を説明していません。
riruzen

3
@riruzenは説明しています:doubleより具体的ではありませんlong。また、メソッドを選択するには、すべての型パラメーターをより具体的にする必要があります。すべてのi(1≤i≤n、n = k)の引数eiの型SiはTiよりも具体的です
Kirill Simonov

+1になるはずです
ティー

0

int値はJavaではdoubleと見なすこともできるためです。手段double a = 3は有効であり、ロングと同じですlong b = 3。そのため、あいまいさが生じています。あなたが呼ぶ

printSum(1, 2);

これら3つすべてが有効であるため、3つすべての方法について混乱しています。

int a = 1;
double b =1;
long c = 1;

Lを最後に置いて、長い値であることを指定できます。例えば:

printSum(1L, 2L);

doubleの場合は変換する必要があります。

printSum((double)1, 2L);

@Erwin Bolwidtのコメントも読む


はい、コンパイラは3つの方法すべてで混乱しています。最初のコンパイラは正確な型を探し、見つからない場合は互換性のある型を探します。この場合、すべて互換性があります。
別のコーダー、

2
私が代わりに3の4つのメソッド宣言を持っている場合は、あいまいさが外れ:公共の静的な無効printSumは(ダブルB int型)(、長いBをint型)公共の静的な無効printSumのpublic static(長い、長いb)は、ボイドprintSumのpublic static void printSum(double a、long b)したがって、私はあなたの答えに同意しますが、それでも質問には答えません。Javaは、間違いがないのに正確な型が利用できない場合、自動型昇格を行います。それがなぜこれら3で曖昧になるのか、私には理解できません
。– riruzen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.