パラメータがリテラルnull値の場合、オーバーロードされたメソッドはどのように選択されますか?


98

私はクイズでこの質問に出くわしました、

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値に割り当てると、文字列が有効になり、Javaおよびほとんどのプログラミング言語の順序が最も近い型に適合してからオブジェクトに適合します。
JonH 2012年


6
どうやらこれは、タイトルを読むだけの人々によって複製として閉じられました。ここでの実際の問題は、「nullとは何か」ではなく、特定のオーバーロードが選択された理由についてです。
interjay 2012年

回答:


102

nullは何も指していないString変数ですか?

null参照は、任意のクラス型の式に変換できます。したがって、の場合String、これは問題ありません。

String x = null;

StringJavaコンパイラが選ぶので、ここに過負荷が選択されているほとんどの特定のあたりとして、過負荷をJLSのセクション15.12.2.5。特に:

非公式な直感は、最初のメソッドによって処理された呼び出しがコンパイル時のタイプエラーなしで他のメソッドに渡される場合、1つのメソッドが他のメソッドよりも具体的であるということです。

2番目のケースでは、どちらの方法も引き続き適用できますが、どちらStringもどちらかStringBuffer一方が他方よりも限定的ではないため、どちらの方法も他方よりも限定的ではないため、コンパイラエラーが発生します。


3
また、文字列オーバーロードはオブジェクトオーバーロードよりも具体的ですか?
user1610015 2012年

12
なぜなら、Object任意の型を取り、それをでラップできるのObjectに対し、a はのみを取ることができるからです。この場合、はタイプに比べてタイプの特定性が高くなります。StringStringStringObject
JonH 2012年

10
@zakSyedより特別な「文字列」または「オブジェクト」を尋ねられたら、どう思いますか?明らかに「ストリング」ですよね?あなたが尋ねられた場合:より特別な「文字列」または「文字列バッファ」は何ですか?答えはありません。どちらも直交する専門分野です。どうすればそれらを選択できますか?次に、どちらを使用するかを明示する必要があります(つまり、null参照をキャストすることによってquestion.method((String)null)
Edwin Dalorzo 2012年

1
@JonSkeet:それは理にかなっています。したがって、基本的には最も具体的なルールに従ってメソッドを探し、どちらがより具体的かを判断できない場合は、コンパイル時エラーがスローされます。
zakSyed 2012年

3
@JonH "null"は参照型です。メソッドの1つがパラメータとしてプリミティブ型(int)を受け取った場合、null型の参照に対して呼び出す適切なメソッドを選択するときに、コンパイラによって考慮されません。「Int」がjava.lang.Integerを意味するのか、プリミティブ型intを意味するのかは混乱を招きます。
Edwin Dalorzo

9

さらに、JLS 3.10.7は、「null」が「null型」のリテラル値であることも宣言しています。したがって、「null」と呼ばれるタイプが存在します。

その後、JLS 4.1は、変数を宣言することができないnull型が存在すると述べていますが、nullリテラルを介してのみ使用できます。後でそれは言う:

null参照は、常に任意の参照型への拡大参照変換を受けることができます。

コンパイラが文字列に拡張することを選択する理由は、ジョンの回答で説明されている可能性があります。


タイプはVoidであると確信しています。
xavierm02

2
@ xavierm02 Java言語仕様ではそのことについては言及されていません。私たち全員があなたの主張を確認できるように、あなたの参照を引用できますか?
Edwin Dalorzo、

私はそのことを決して読んだことがない。しかし、私はこの夏にJavaをいくつか実行しました。Objectを取得するメソッドをVoidオブジェクトを取得するメソッドでオーバーロードできます。
xavierm02

それはタイプというよりもクラスです。
xavierm02

4
@ xavierm02もちろんできます。Javaのメソッドは、他の任意の型でオーバーロードできます。それでも、それは質問や私の回答とは関係ありません。
Edwin Dalorzo、

2

を値に割り当てるstringと、null値が有効になり、Javaとほとんどのプログラミング言語の順序が最も近い型に適合してからオブジェクトに適合します。


2

タイトルの質問に答えるために:nullStringでもでもありませんObjectが、どちらかへの参照をnull

実際にこのコードがコンパイルされても驚いています。以前に同様のことを試みたところ、呼び出しがあいまいであるというコンパイラエラーが発生しました。

ただし、この場合、コンパイラはフードチェーンで最も低いメソッドを選択しているようです。それはあなたがあなたを助けるためにあなたがメソッドの最も一般的なバージョンを望んでいないことを前提としています。

ただし、この(一見)まったく同じシナリオでコンパイラエラーが発生した例を掘り下げることができるかどうかを確認する必要がありますが...]

編集: なるほど。私が作成したバージョンでは、a Stringとを受け入れる2つのオーバーロードメソッドがありましたInteger。このシナリオでは、「最も具体的な」パラメータはありません(ObjectおよびStringがないため、コードとは異なり、それらを選択することはできません。

とてもクールな質問です!


nullはどちらでもありませんが、両方に割り当てることができます。それが違いであり、なぜコンパイルしないのかをコンパイルします。これは完全に有効なコードです。
JonH 2012年

0

文字列型はオブジェクト型よりも具体的です。Integer型を取るメソッドをもう1つ追加するとします。

public void method(Integer i) {
      System.out.println("Integer Version");
   }

次に、呼び出しがあいまいであることを示すコンパイラエラーが表示されます。今のところ、同じ優先順位を持つ2つの同等に特定のメソッド。


0

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);
    }
}

結果:メソッドfun(C)は、MyTestタイプにはあいまいです

このケースをよりよく理解するのに役立つことを願っています。


0

ソース: https : //docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
概念:最も具体的なメソッド
説明:複数のメンバーメソッドにアクセス可能であり、かつメソッド呼び出しに適用できる場合は、実行時メソッドディスパッチの記述子を提供するために1つを選択する必要があります。Javaプログラミング言語は、最も具体的なメソッドが選択されるという規則を使用します。nullを特定の型にキャストすると、目的のメソッドが自動的に呼び出されます。


最初の例では、StringはObjectを拡張するため、「最も具体的」なのはStringを取得するメソッドですが、2番目の例では、StringとStringBufferの両方がObjectを拡張するので、それらは等しく固有であるため、コンパイラは選択できません
David Kerr

-1

私はどちらとも言いません。NULLは値ではなく状態です。これに関する詳細については、このリンクをチェックしてください(この記事はSQLに適用されますが、質問にも役立つと思います)。


nullJLSで定義されているように、非常に間違いなく値です。
Sotirios Delimanolis 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.