Как перегруженный метод выбирается, когда параметр является буквальным нулевым значением?

Я столкнулся с этим вопросом в викторине,

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

Вывод этой программы - "String Version". Но я не мог понять, почему передача 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);
   }
}

он дает ошибку компиляции: "Метод метода (StringBuffer) неоднозначен для типа MoneyCalc"

Ответ 1

Является ли null переменной String, указывающей на ничего?

Нулевая ссылка может быть преобразована в выражение любого типа класса. Поэтому в случае String это нормально:

String x = null;

Перегрузка String здесь выбрана потому, что компилятор Java выбирает наиболее специфическую перегрузку, согласно раздел 15.12.2.5 JLS. В частности:

Неофициальная интуиция заключается в том, что один метод более конкретный, чем другой, если любой вызов, обработанный первым методом, может быть передан другому, без ошибки типа компиляции.

В вашем втором случае оба метода все еще применимы, но ни String, ни StringBuffer не является более конкретным, чем другой, поэтому ни один из методов не является более конкретным, чем ошибка компилятора.

Ответ 2

Кроме того, JLS 3.10.7 также объявляет, что "null" является буквальным значением "нулевого типа". Поэтому существует тип, называемый "null".

Позже JLS 4.1 утверждает, что существует нулевой тип, который невозможно объявить переменными, но вы можете использовать его через только нулевой литерал. Позже он говорит:

Нулевая ссылка всегда может подвергаться расширению ссылочного преобразования к любому ссылочному типу.

Почему компилятор хочет расширить его до строки, вполне можно объяснить в Jon answer.

Ответ 3

Вы можете присвоить значение string значению null, чтобы оно было действительным, а порядок для java и большинства языков программирования подходит для ближайшего типа, а затем для объекта.

Ответ 4

Чтобы ответить на вопрос в заголовке: null не является ни String, ни Object, но ссылка на либо может быть назначена на null.

Я действительно удивлен, что этот код даже компилируется. Я попробовал что-то подобное ранее, и я получил ошибку компилятора, говоря, что вызов был неоднозначным.

Однако в этом случае, похоже, компилятор выбирает метод, который является самым низким в пищевой цепи. Предполагая, что вам нужна наименее общая версия метода, чтобы помочь вам.

Мне нужно посмотреть, могу ли я выкопать пример, где я получил ошибку компилятора в этом (по-видимому) точно таком же сценарии, хотя...]

EDIT: Я вижу. В версии, которую я сделал, у меня было два перегруженных метода, принимающих String и Integer. В этом случае нет "наиболее специфического" параметра (как в Object и String), поэтому он не может выбирать между ними, в отличие от вашего кода.

Очень крутой вопрос!

Ответ 5

Поскольку тип String более специфичен, чем тип объекта. Предположим, вы добавили еще один метод, который принимает тип Integer.

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

Затем вы получите сообщение об ошибке компилятора, говорящее, что вызов неоднозначен. Как сейчас мы имеем два одинаково определенных метода с одинаковым приоритетом.

Ответ 6

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

Надеюсь, что это поможет лучше понять этот случай.

Ответ 7

Я бы тоже сказал. NULL - это состояние, а не значение. Ознакомьтесь с этой ссылкой для получения дополнительной информации об этом (статья применима к SQL, но я думаю, что это помогает и с вашим вопросом).