Wie wird eine überladene Methode ausgewählt, wenn ein Parameter der Literal-Nullwert ist?


98

Ich bin auf diese Frage in einem Quiz gestoßen,

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

Die Ausgabe dieses Programms ist "String Version". Aber ich konnte nicht verstehen, warum das Übergeben einer Null an eine überladene Methode die String-Version auswählte. Ist null eine String-Variable, die auf nichts zeigt?

Wenn der Code jedoch in geändert wird,

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

Es wird ein Kompilierungsfehler ausgegeben, der besagt: "Die Methodenmethode (StringBuffer) ist für den Typ MoneyCalc nicht eindeutig."


Sie können einem Nullwert eine Zeichenfolge zuweisen, damit diese gültig ist und die Reihenfolge für Java und die meisten Programmiersprachen dem nächstgelegenen Typ und dann dem Objekt entspricht.
JonH


6
Anscheinend wurde dies von Personen, die nur den Titel lesen, als Duplikat geschlossen. Die eigentliche Frage hier ist, warum eine bestimmte Überladung gewählt wurde, nicht "was ist null".
Interjay

Antworten:


102

Ist null eine String-Variable, die auf nichts zeigt?

Eine Nullreferenz kann in einen Ausdruck eines beliebigen Klassentyps konvertiert werden. Im Fall von Stringist dies also in Ordnung:

String x = null;

Die StringÜberladung wird hier ausgewählt, weil der Java-Compiler die spezifischste Überladung gemäß Abschnitt 15.12.2.5 des JLS auswählt . Bestimmtes:

Die informelle Intuition ist, dass eine Methode spezifischer als eine andere ist, wenn ein Aufruf, der von der ersten Methode verarbeitet wird, ohne einen Fehler vom Typ der Kompilierungszeit an die andere weitergegeben werden könnte.

In Ihrem zweiten Fall sind beide Methoden weiterhin anwendbar, aber weder Stringnoch StringBufferspezifischer als die andere, daher ist keine Methode spezifischer als die andere, daher der Compilerfehler.


3
Und wie ist die Stringüberladung spezifischer als die Objektüberladung?
user1610015

12
Denn ein Objectkann jeden Typ nehmen und in einen einwickeln Object, während ein Stringnur einen nehmen kann String. In diesem Fall Stringist ein Typ spezifischer als der ObjectTyp.
JonH

10
@zakSyed Wenn Sie gefragt würden, was "String" oder "Object" spezialisierter ist, was würden Sie sagen? Offensichtlich "String", richtig? Wenn Sie gefragt wurden: Was ist spezialisierter "String" oder "StringBuffer"? Es gibt keine Antwort, beide sind orthogonale Spezialisierungen. Wie können Sie zwischen ihnen wählen? Dann müssen Sie explizit angeben, welche Sie möchten (dh indem Sie Ihre question.method((String)null)
Nullreferenz eingeben

1
@ JonSkeet: Das macht Sinn. Im Grunde sucht es nach einer Methode, die der spezifischsten Regel entspricht, und wenn es nicht entscheiden kann, welche spezifischer ist, würde es einen Fehler bei der Kompilierung auslösen.
zakSyed

3
@JonH "null" ist ein Referenztyp. Wenn eine der Methoden einen primitiven Typ als Parameter (dh int) empfängt, wird dies vom Compiler nicht einmal berücksichtigt, wenn die richtige Methode zum Aufrufen für eine Referenz vom Typ null ausgewählt wird. Es ist verwirrend, ob Sie mit "Int" java.lang.Integer oder den primitiven Typ int gemeint haben.
Edwin Dalorzo

9

Darüber hinaus deklariert JLS 3.10.7, dass "null" ein Literalwert vom Typ "null" ist. Daher gibt es einen Typ namens "null".

Später gibt JLS 4.1 an , dass es einen Nulltyp gibt, dessen Variablen nicht deklariert werden können. Sie können ihn jedoch nur über das Nullliteral verwenden. Später heißt es:

Die Nullreferenz kann immer einer erweiterten Referenzkonvertierung in einen beliebigen Referenztyp unterzogen werden.

Warum der Compiler es auf String erweitert, wird in Jons Antwort erklärt .


Ich bin mir ziemlich sicher, dass dieser Typ Void ist.
Xavierm02

2
@ xavierm02 Dies wird in der Java-Sprachspezifikation nicht erwähnt. Können Sie Ihre Referenz zitieren, damit wir alle Ihren Anspruch überprüfen können?
Edwin Dalorzo

Ich habe das Ding nie gelesen. Aber ich habe diesen Sommer Java gemacht und Sie können eine Methode, die ein Objekt nimmt, mit einer Methode überladen, die ein Void-Objekt nimmt.
Xavierm02

Es ist eher eine Klasse als ein Typ.
Xavierm02

4
@ xavierm02 Natürlich kannst du. Sie können eine Methode in Java mit einem beliebigen anderen Typ überladen. Das hat jedoch nichts mit der Frage oder meiner Antwort zu tun.
Edwin Dalorzo

2

Sie können stringeinem nullWert ein zuweisen , damit er gültig ist und die Reihenfolge für Java und die meisten Programmiersprachen dem nächstgelegenen Typ und dann dem Objekt entspricht.


2

Um die Frage im Titel zu beantworten: nullist weder ein Stringnoch ein Object, aber ein Verweis auf eines kann zugewiesen werden null.

Ich bin tatsächlich überrascht, dass dieser Code sogar kompiliert wird. Ich habe zuvor etwas Ähnliches ausprobiert und einen Compilerfehler erhalten, der besagt, dass der Aufruf nicht eindeutig ist.

In diesem Fall scheint der Compiler jedoch die Methode zu wählen, die in der Nahrungskette am niedrigsten ist. Es wird davon ausgegangen, dass Sie die am wenigsten generische Version der Methode möchten, um Ihnen zu helfen.

Ich werde sehen müssen, ob ich das Beispiel ausgraben kann, in dem ich in diesem (scheinbar) genau gleichen Szenario einen Compilerfehler erhalten habe ...]

EDIT: Ich verstehe. In der von mir erstellten Version hatte ich zwei überladene Methoden, die a Stringund an akzeptierten Integer. In diesem Szenario gibt es keinen "spezifischsten" Parameter (wie in Objectund String), daher kann er im Gegensatz zu Ihrem Code nicht zwischen diesen wählen.

Sehr coole Frage!


null ist keines von beiden, aber es kann beiden zugewiesen werden, das ist der Unterschied und es wird kompiliert, warum nicht - es ist absolut gültiger Code.
JonH

0

Der String-Typ ist spezifischer als der Objekttyp. Angenommen, Sie fügen eine weitere Methode hinzu, die einen Integer-Typ annimmt.

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

Dann wird ein Compilerfehler angezeigt, der besagt, dass der Aufruf nicht eindeutig ist. Wie jetzt haben wir zwei gleich spezifische Methoden mit gleicher Priorität.


0

Der Java-Compiler gibt den meisten abgeleiteten Klassentyp an, um null zuzuweisen.

Hier ist das Beispiel, um es zu verstehen:

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

Ausgabe: Klasse B

andererseits:

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

Ergebnis: Die Methode fun (C) ist für den Typ MyTest nicht eindeutig

Hoffe, es wird helfen, diesen Fall besser zu verstehen.


0

Quelle: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
Konzept: Spezifischste Methode
Erläuterung: Wenn auf mehr als eine Mitgliedsmethode zugegriffen werden kann und Für einen Methodenaufruf muss eine ausgewählt werden, um den Deskriptor für den Laufzeitmethodenversand bereitzustellen. Die Programmiersprache Java verwendet die Regel, dass die spezifischste Methode ausgewählt wird. Versuchen Sie, die Null auf einen bestimmten Typ zu übertragen, und die gewünschte Methode wird automatisch aufgerufen.


Im ersten Beispiel erweitert String Object, daher ist "am spezifischsten" die Methode, die String verwendet. Im zweiten Beispiel erweitern String und StringBuffer beide Object, sodass sie gleichermaßen spezifisch sind und der Compiler daher keine Auswahl treffen kann
David Kerr

-1

Ich würde auch nicht sagen. NULL ist ein Zustand, kein Wert. Weitere Informationen hierzu finden Sie unter diesem Link (der Artikel gilt für SQL, aber ich denke, er hilft auch bei Ihrer Frage).


nullist definitiv ein Wert, wie im JLS definiert.
Sotirios Delimanolis
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.