クイズでこの質問に出くわしました。
public class MoneyCalc {
public void method(Object o) {
System.out.println("Object Verion");
}
public void method(String s) {
System.out.println("String Version");
}
public static void main(String args[]) {
MoneyCalc question = new MoneyCalc();
question.method(null);
}
}
このプログラムの出力は「文字列バージョン」です。しかし、オーバーロードされたメソッドにnullを渡すと文字列バージョンが選択された理由を理解できませんでした。 nullは何も指し示していないString変数ですか?
ただし、コードが変更されると、
public class MoneyCalc {
public void method(StringBuffer sb) {
System.out.println("StringBuffer Verion");
}
public void method(String s) {
System.out.println("String Version");
}
public static void main(String args[]) {
MoneyCalc question = new MoneyCalc();
question.method(null);
}
}
「メソッドmethod(StringBuffer)はMoneyCalc型ではあいまいです」というコンパイルエラーが発生します。
Nullは何も指し示していないString変数ですか?
Null参照は、任意のクラスタイプの式に変換できます。 String
の場合、これは問題ありません。
String x = null;
ここでString
オーバーロードが選択されているのは、Javaコンパイラーが最も具体的なオーバーロードを選択するため JLSのセクション15.12.2.5 。特に:
非公式の直観は、最初のメソッドによって処理された呼び出しがコンパイル時の型エラーなしで他のメソッドに渡される場合、あるメソッドは別のメソッドよりも具体的であるということです。
2番目のケースでは、両方のメソッドがまだ適用可能ですが、String
とStringBuffer
のどちらももう一方よりも具体的ではないため、どちらのメソッドもより具体的ではないため、コンパイラエラーになります。
さらに、 JLS 3.10.7 は、「null」が「null type」のリテラル値であることも宣言します。したがって、「null」と呼ばれるタイプが存在します。
後で、 JLS 4.1 は、変数を宣言することが不可能なnullタイプが存在することを示していますが、nullリテラルでのみ使用できます。後でそれは言います:
Null参照は、任意の参照タイプへの拡大参照変換を常に受けることができます。
コンパイラがそれをStringに拡張することを選択する理由は、 Jonの答え で説明されているかもしれません。
string
をnull
の値に割り当てると、有効になり、Javaおよびほとんどのプログラミング言語の順序が最も近い型に適合し、次にオブジェクト。
タイトルの質問に答えるには:null
はString
でもObject
でもありませんが、どちらかへの参照をnull
に割り当てることができます。
実際、このコードがコンパイルされても驚いています。以前同様のことを試してみましたが、呼び出しがあいまいであるというコンパイラエラーが発生しました。
ただし、この場合、コンパイラーはフードチェーンで最も低い方法を選択しているようです。あなたが手助けするために、メソッドの最も一般的なバージョンが欲しいと仮定しています。
この(一見)完全に同じシナリオでコンパイラエラーが発生した例を掘り下げることができるかどうかを確認する必要がありますが...]
編集:なるほど。作成したバージョンでは、String
とInteger
を受け入れるオーバーロードメソッドが2つありました。このシナリオでは、「Object
およびString
のように」「最も具体的な」パラメーターはないため、コードとは異なり、それらを選択することはできません。
非常にクールな質問!
出典:https://docs.Oracle.com/javase/specs/jls/se8/html/jls-15.html# jls-15.12.2.5
概念:最も具体的な方法
説明:複数のメンバーメソッドがアクセス可能であり、メソッド呼び出しに適用できる場合、ランタイムメソッドディスパッチの記述子を提供するために1つを選択する必要があります。 Javaプログラミング言語は、最も具体的なメソッドが選択されるというルールを使用します。特定のタイプにnullをキャストすると、希望するメソッドが自動的に呼び出されます。
文字列型はオブジェクト型よりも具体的です。整数型をとるメソッドをもう1つ追加するとします。
public void method(Integer i) {
System.out.println("Integer Version");
}
次に、呼び出しがあいまいであることを示すコンパイラエラーが表示されます。今のように、同じ優先順位を持つ2つの等しく特定のメソッド。
Javaコンパイラは、nullを割り当てるためのほとんどの派生クラスタイプを提供します。
これを理解するための例を次に示します。
class A{
public void methodA(){
System.out.println("Hello methodA");
}
}
class B extends A{
public void methodB(){
System.out.println("Hello methodB");
}
}
class C{
public void methodC(){
System.out.println("Hello methodC");
}
}
public class MyTest {
public static void fun(B Obj){
System.out.println("B Class.");
}
public static void fun(A Obj){
System.out.println("A Class.");
}
public static void main(String[] args) {
fun(null);
}
}
出力: Bクラス。
一方:
public class MyTest {
public static void fun(C Obj){
System.out.println("B Class.");
}
public static void fun(A Obj){
System.out.println("A Class.");
}
public static void main(String[] args) {
fun(null);
}
}
結果:メソッドMyTestのメソッドfun(C)はあいまいです
このケースをよりよく理解するのに役立つことを願っています。