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

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

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

zakSyed
источник
Вы можете присвоить строку нулевому значению, чтобы она была действительной, а порядок для java и большинства языков программирования соответствовал ближайшему типу, а затем объекту.
JonH
6
Очевидно, это было закрыто как дубликат людьми, которые читали только заголовок. Фактический вопрос здесь заключается в том, почему была выбрана конкретная перегрузка, а не «что является нулевым».
Interjay

Ответы:

102

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

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

String x = null;

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

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

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

Джон Скит
источник
3
И чем перегрузка String более специфична, чем перегрузка Object?
user1610015
12
Поскольку an Objectможет принимать любой тип и оборачивать его в Object, тогда как a Stringможет принимать только a String. В этом случае Stringэто более конкретный тип по сравнению с Objectтипом.
JonH
10
@zakSyed Если бы вас спросили, что такое более специализированный "String" или "Object", что бы вы ответили? Очевидно, «струна»? Если вас спросят: что такое более специализированный String или StringBuffer? Нет ответа, это обе ортогональные специализации, как выбрать между ними? Тогда вы должны четко указать, какой из них вы хотите (например, question.method((String)null)
приведя
1
@JonSkeet: В этом есть смысл. Таким образом, в основном он ищет метод в соответствии с наиболее конкретным правилом, и если он не может решить, какой из них более конкретен, он выдаст ошибку времени компиляции.
zakSyed
3
@JonH "null" - это ссылочный тип, если один из методов получает в качестве параметра примитивный тип (например, int), он даже не будет учитываться компилятором при выборе правильного метода для вызова для ссылки типа null. Это сбивает с толку, если под «Int» вы имели в виду java.lang.Integer или если вы имели в виду примитивный тип int.
Эдвин Далорцо
9

Кроме того, JLS 3.10.7 также объявляет, что «null» является буквальным значением «нулевого типа». Следовательно, существует тип под названием «null».

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

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

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

Эдвин Далорцо
источник
Я совершенно уверен, что это Пустота.
xavierm02
2
@ xavierm02 Об этом не упоминается в спецификации языка Java. Можете ли вы процитировать свой отзыв, чтобы мы все могли проверить ваше утверждение?
Эдвин Далорцо,
Я никогда не читал эту вещь. Но этим летом я сделал немного Java, и вы можете перегрузить метод, принимающий Object, методом, принимающим объект Void.
xavierm02
Это больше класс, чем тип.
xavierm02
4
@ xavierm02 Конечно, можешь. Вы можете перегрузить метод в Java любым другим типом, который хотите. Это, тем не менее, не имеет ничего общего с вопросом или моим ответом.
Эдвин Далорцо
2

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

JonH
источник
2

Чтобы ответить на вопрос в заголовке: не nullявляется ни a, Stringни an Object, но ссылку на любой из них можно присвоить null.

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

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

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

РЕДАКТИРОВАТЬ: Понятно. В сделанной мной версии у меня было два перегруженных метода, принимающих a Stringи an Integer. В этом сценарии нет «наиболее конкретного» параметра (например, Objectи String), поэтому он не может выбирать между ними, в отличие от вашего кода.

Очень классный вопрос!

Астери
источник
null не является ни тем, ни другим, но он может быть назначен обоим, в этом разница, и он будет компилировать, почему бы и нет - это абсолютно правильный код.
JonH
0

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

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

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

BSingh
источник
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 Class.

с другой стороны:

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
Концепция: наиболее конкретный метод
Объяснение: если доступно более одного метода-члена, и применимо к вызову метода, необходимо выбрать один, чтобы предоставить дескриптор для отправки метода во время выполнения. В языке программирования Java используется правило, согласно которому выбирается наиболее конкретный метод. Попробуйте привести значение null к определенному типу, и нужный вам метод будет вызван автоматически.

Депутат
источник
В первом примере, String расширяет объект, так что «наиболее специфичный» метод принимает строку, а во втором примере, String и StringBuffer как расширить объект, поэтому они одинаково специфичны и поэтому компилятор не может сделать выбор
David Kerr
-1

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

Северный полюс
источник
nullопределенно является значением, как определено в JLS.
Сотириос Делиманолис