35

今日、自動ボクシングによってメソッドのオーバーロード解決が曖昧になることがあることに気付きました。最も単純な例は次のようです。

public class Test {
    static void f(Object a, boolean b) {}
    static void f(Object a, Object b) {}

    static void m(int a, boolean b) { f(a,b); }
}

コンパイルすると、次のエラーが発生します。

Test.java:5: reference to f is ambiguous, both method
    f(java.lang.Object,boolean) in Test and method
    f(java.lang.Object,java.lang.Object) in Test match

static void m(int a, boolean b) { f(a, b); }
                                  ^

このエラーの修正は簡単です: 明示的な自動ボクシングを使用するだけです:

static void m(int a, boolean b) { f((Object)a, b); }

期待どおりに最初のオーバーロードを正しく呼び出します。

では、なぜオーバーロードの解決に失敗したのでしょうか? コンパイラが最初の引数を自動ボックス化せず、2 番目の引数を正常に受け入れなかったのはなぜですか? 自動ボクシングを明示的に要求する必要があったのはなぜですか?

4

6 に答える 6

36

最初の引数を自分で Object にキャストすると、コンパイラはオートボクシングを使用せずにメソッドを一致させます (JLS3 15.12.2):

最初のフェーズ (§15.12.2.2) では、ボックス化またはボックス化解除の変換、または可変アリティ メソッド呼び出しの使用を許可せずに、オーバーロードの解決を実行します。このフェーズで適用可能なメソッドが見つからない場合、処理は 2 番目のフェーズに進みます。

明示的にキャストしないと、一致するメソッドを見つけようとする第 2 フェーズに進み、オートボクシングが可能になります。2 番目の引数はブール値またはオブジェクトと一致する可能性があるため、実際にはあいまいです。

2 番目のフェーズ (§15.12.2.3) では、ボックス化とボックス化解除を許可しながらオーバーロードの解決を実行しますが、可変アリティ メソッドの呼び出しは引き続き使用できません。

2 番目のフェーズで、ブール引数のオートボクシングが必要ないため、コンパイラが 2 番目の方法を選択しないのはなぜですか? 2 つの一致するメソッドが見つかった後は、最初にそれらを一致させるために行われたボックス化またはボックス化解除に関係なく、サブタイプ変換のみが 2 つの最も具体的なメソッドを決定するために使用されるためです (§15.12.2.5)。

また、コンパイラは、必要なオート (アン) ボクシングの数に基づいて、最も具体的な方法を常に選択できるとは限りません。それでもあいまいなケースになる可能性があります。たとえば、これはまだあいまいです。

public class Test {
    static void f(Object a, boolean b) {}
    static void f(int a, Object b) {}

    static void m(int a, boolean b) { f(a, b); } // ambiguous
}

マッチング方法 (コンパイル時のステップ 2) を選択するためのアルゴリズムは固定されており、JLS で説明されていることに注意してください。フェーズ 2 に入ると、選択的な自動ボックス化またはボックス化解除はありません。コンパイラは、アクセス可能 (これらの場合は両方のメソッド) かつ適用可能 (ここでも 2 つのメソッド) であるすべてのメソッドを見つけ、ボックス化/ボックス化解除を確認せずに最も具体的なメソッドを選択しますが、ここではあいまいです。

于 2009-02-01T19:47:31.137 に答える
5

コンパイラ最初の引数を自動ボックス化しました。それが完了すると、ブール値またはオブジェクトのいずれかと見なされる可能性があるため、あいまいなのは 2 番目の引数です。

このページでは、オートボクシングのルールと呼び出すメソッドの選択について説明します。コンパイラは、最初にオートボクシングをまったく使用せずにメソッドを選択しようとします。これは、ボクシングとアンボクシングによってパフォーマンスが低下するためです。この場合のように、ボックス化に頼らずにメソッドを選択できない場合は、そのメソッドへのすべての引数に対してボックス化が検討されます。

于 2009-02-01T19:38:39.390 に答える
3

f(a, b )と言うと、コンパイラはどの関数を参照すべきか混乱します。

これは、aintであるためですが、 fで期待される引数は Object です。したがって、コンパイラはaを Object に変換することを決定します。問題は、 aをオブジェクトに変換できる場合bも変換できることです。

これは、関数呼び出しがどちらの定義も参照できることを意味します。これにより、呼び出しがあいまいになります。

a を手動でオブジェクトに変換すると、コンパイラは最も近い一致を探して参照します。

コンパイラは、「ボックス化/ボックス化解除の変換をできるだけ少なくする」ことで到達できる関数を選択しなかったのはなぜですか?

次のケースを参照してください。

f(boolean a, Object b)
f(Object a , boolean b)

f(boolean a, boolean b) のように呼び出す場合、どの関数を選択する必要がありますか? 曖昧ですよね?同様に、多くの引数が存在する場合、これはより複雑になります。そのため、コンパイラは代わりに警告を表示することを選択しました。

プログラマーが実際にどの関数を呼び出すつもりだったのかを知る方法がないため、コンパイラーはエラーを出します。

于 2009-02-01T19:36:59.083 に答える
2

では、なぜオーバーロードの解決に失敗したのでしょうか? コンパイラが最初の引数を自動ボックス化せず、2 番目の引数を正常に受け入れなかったのはなぜですか? 自動ボクシングを明示的に要求する必要があったのはなぜですか?

2 番目の引数が正常に受け入れられませんでした。「ブール値」もオブジェクトにボックス化できることに注意してください。ブール引数を Object に明示的にキャストすることもでき、それはうまくいきました。

于 2009-02-01T19:35:59.600 に答える
2

http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448を参照してください。

呼び出すメソッドを見つけるためにボクシングが必要ないため、キャストが役立ちます。キャストがなければ、2 番目の試みはボクシングを許可することであり、ブール値もボックス化できます。

人々に推測させるよりも、何が起こるかを明確でわかりやすい仕様にする方が良い.

于 2009-02-01T19:46:06.037 に答える
1

Java コンパイラは、オーバーロードされたメソッドとコンストラクタを段階的に解決します。最初のフェーズ [§15.12.2.2] では、[§4.10] をサブタイプすることによって適用可能なメソッドを識別します。この例では、int は Object のサブタイプではないため、どちらの方法も適用できません。

第 2 段階 [§15.12.2.3] では、コンパイラは、オートボクシングとサブタイピングの組み合わせであるメソッド呼び出し変換 [§5.3] によって適用可能なメソッドを識別します。int 引数は、どちらのオーバーロードでも、Object のサブタイプである Integer に変換できます。boolean 引数は、最初のオーバーロードの変換を必要とせず、2 番目のオブジェクトのサブタイプである Boolean に変換できます。したがって、両方の方法が第 2 段階に適用できます。

複数の方法が適用できるため、コンパイラは最も具体的な方法を決定する必要があります [§15.12.2.5]。引数の型ではなく、パラメーターの型を比較し、自動ボックス化しません。Object と boolean は無関係な型であるため、同じように具体的であると見なされます。どちらのメソッドも他のメソッドより具体的ではないため、メソッド呼び出しがあいまいです。

あいまいさを解決する 1 つの方法は、boolean パラメーターを Object のサブタイプである Boolean 型に変更することです。最初のオーバーロードは、2 番目のオーバーロードよりも (該当する場合) より具体的です。

于 2012-01-31T21:42:12.090 に答える