Подтвердить что ты не робот

Перегрузка метода и выбор наиболее конкретного типа

Пример кода:

    public class OverloadingTest {

       public static void test(Object obj){
           System.out.println("Object called");
       }

       public static void test(String obj){
           System.out.println("String called");
       }

       public static void main(String[] args){
           test(null);
           System.out.println("10%2==0 is "+(10%2==0));
           test((10%2==0)?null:new Object());
           test((10%2==0)?null:null);
   }

И результат:

Строка называется
10% 2 == 0 истинно
Объект, названный
Строка называется

Первый вызов test(null) вызывает метод с аргументом String, что понятно в соответствии с The Java Language Specification.

1) Может ли кто-нибудь объяснить мне, на каком основании test() вызывается в предыдущих вызовах?

2) Снова, когда мы положим, скажем, условие if:

    if(10%2==0){
        test(null);
    }
    else
    {
        test(new Object());
    }

Он всегда вызывает метод с аргументом String.

Будет ли компилятор вычислять выражение (10%2) при компиляции? Я хочу знать, вычисляются ли выражения во время компиляции или времени выполнения. Спасибо.

4b9b3361

Ответ 1

Java использует раннее связывание. Наиболее специфический метод выбирается во время компиляции. Наиболее специфический метод выбирается по количеству параметров и типу параметров. В этом случае количество параметров не имеет значения. Это оставляет нам тип параметров.

Какой тип имеют параметры? Оба параметра являются выражениями, используя тернарный условный оператор. Вопрос сводится к следующему: какой тип возвращает условный тернарный оператор? Тип вычисляется во время компиляции.

Ниже приведены два выражения:

(10%2==0)? null : new Object(); // A
(10%2==0)? null : null; // B

Правила оценки типов перечислены здесь. В B легко, оба термина точно совпадают: null будет возвращен (любой тип, который может быть) (JLS: "Если второй и третьи операнды имеют один и тот же тип (который может быть нулевым типом), то это тип условного выражения." ). В A второе слагаемое относится к определенному классу. Поскольку это более специфично, и null может быть заменен объектом класса Object, тип всего выражения равен Object (JLS: "Если один из второго и третьего операндов имеет нулевой тип и тип другого является ссылочным типом, тогда тип условного выражения является ссылочным типом." ).

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

Пример с if, который вы даете, отличается: вы вызываете методы с объектами двух разных типов. Терминальный условный оператор всегда вычисляется одним типом во время компиляции, которое соответствует двум терминам.

Ответ 2

test((10%2==0)?null:new Object());

То же, что и:

Object o;

if(10%2==0)
    o=null;
else
    o=new Object();

test(o);

Так как тип o равен Object (точно так же, как тип (10%2==0)?null:new Object()) test(Object) будет всегда вызываться. Значение o не имеет значения.

Ответ 3

JLS 15.25:

Тип условного выражения определяется следующим образом:

     

[...]

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

[...]

Итак, тип

10 % 2 == 0 ? null : new Object();

- объект.

Ответ 4

Ваш ответ: Время выполнения, потому что во время выполнения указать параметр - это экземпляр String или нет, поэтому во время компиляции его не найти.

Ответ 5

Это действительно хороший вопрос.

Позвольте мне прояснить ваш код, который вы написали выше.

  • В первом вызове метода

Тест (нуль);

В этом null будет преобразован в строковый тип, поэтому вызываем test(String obj), в соответствии с JLS вы убеждены в вызове.

  • Во втором вызове метода

test ((10% 2 == 0)? null: new Object());

Что будет возвращать логическое "истинное" значение. Таким образом, первое логическое значение "true" будет автоматически добавлено в объект класса Boolean Wrapper. Boolean wrapper Объект находит наилучшее совпадение с вашим параметром new Object() в тернарном операторе. И метод вызывает объект как параметр, поэтому он вызывает следующий метод

public static void test (Object obj)

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

test ((10% 2 == 0)? new Object(): "stringObj" );

test ((10% 2 == 0)? new Object(): null);

test ((10% 2 == 0)? "stringObj": null);

  • Наконец, в последнем случае, когда вы вызываете следующий код.

тест ((10% 2 == 0) NULL: NULL);

На этот раз он возвращается как логическое "истинное" значение, и он снова будет следовать тем же методам, что и объяснено выше. Но на этот раз в вашем тернарном операторе нет параметра new Object(). Таким образом, он будет автоматически добавлен в null Object. Опять же он следует за тем же вызовом метода, что и ваш первый вызов метода.

  • В последнем случае, когда вы запросили код, если вы введете инструкцию if .. else. Затем также компилятор делает справедливое решение с кодом.

если (10% 2 == 0) {         Тест (нуль);      }

Здесь все время ваше условие if истинно и вызывает этот код test(null). Поэтому все время он вызывает первый метод test(String obj) со String как параметр, как описано выше.

Ответ 6

Я думаю, ваша проблема в том, что вы делаете неправильное предположение, ваши выражения:

test((10%2==0)?null:new Object());

и

test((10%2==0)?null:null;

Всегда будет вызывать тест (null), и поэтому они пройдут тест (объект).

Ответ 7

как @Banthar упоминает, что оператор ?: присваивает значение переменной сначала, а затем оценивает условие. С другой стороны, условие if, которое вы упомянули, всегда возвращает true, поэтому компилятор заменит весь блок if-else только телом if.

Ответ 8

1) метод test() определяется типом параметра во время компиляции:

test((Object) null);
test((Object)"String");

вывод:

Object called
Object called

2) Компилятор даже умнее, скомпилированный код эквивалентен просто:

test(null);

вы можете проверить байт-код с помощью javap -c:

   0: aconst_null   
   1: invokestatic  #6                  // Method test:(Ljava/lang/String;)V
   4: return  

Ответ 9

Это то, что Характеристики языка Java говорят о проблеме.

Если более чем одно объявление метода доступно и доступно к вызову метода, необходимо выбрать один для обеспечения дескриптор для отправки времени выполнения. Программирование на Java язык использует правило, в котором выбран наиболее специфический метод.

Это тестовый (String) метод в вашем случае.

И из-за этого, если вы добавите...

public static void test(Integer obj){
           System.out.println("Ingeter called");
       }

появится ошибка компиляции. Тест метода (String) неоднозначен для типа OverloadingTest.

Так же, как JLS говорит:

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

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