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

Пример кода:

    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) во время компиляции? Я хочу знать, вычисляются ли выражения во время компиляции или во время выполнения. Спасибо.


person AllTooSir    schedule 20.02.2012    source источник
comment
test((10%2==0)?null:null; Последняя строка нуждается в закрытии), а основной метод не закрыт.   -  person adranale    schedule 20.02.2012


Ответы (9)


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

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

Даны два выражения:

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

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

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

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

person Hauke Ingmar Schmidt    schedule 20.02.2012
comment
но как насчет состояния; это вообще не имеет значения? Условие верно в обоих случаях; и в соответствии с правилами тернарного оператора в качестве ответа должен быть выбран нуль. - person Ankit; 28.10.2012
comment
Да, но это не относится к вопросу, какой тип имеет все выражение. Конкретный результат оценивается во время выполнения, а тип термина — во время компиляции. Тип результата, конечно, может быть другим, в зависимости от того, что делает выбранный термин. Таким образом, условие не имеет значения, поскольку оно не используется для оценки типа, а только для оценки результата. - person Hauke Ingmar Schmidt; 29.10.2012
comment
Это становится особенно очевидным, поскольку условие также может быть выражением, которое может оцениваться только во время выполнения. В качестве несколько теоретического примера: new Random().nextBoolean(). - person SebastianH; 09.01.2016
comment
как я могу вызвать наименее конкретный метод ?? - person Sagar Nayak; 30.06.2017

JLS 15.25:

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

[...]

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

[...]

Итак, тип

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

является Объектом.

person assylias    schedule 20.02.2012
comment
... Итак, тип (10%2==0)?null:new Object() — это Object. - person Gil Vegliach; 16.08.2014

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 не имеет значения.

person Piotr Praszmo    schedule 20.02.2012

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

person Sam    schedule 20.02.2012

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

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

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

тест (нулевой);

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

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

тест((10%2==0)?null:новый объект());

Который вернет логическое «истинное» значение. Таким образом, первое логическое «истинное» значение будет автоматически преобразовано в объект класса Boolean Wrapper. Объект-оболочка Boolean находит наилучшее соответствие с вашей опцией new Object() в тернарном операторе. И метод вызывает с Object в качестве параметра, поэтому он вызывает следующий метод

общедоступный статический тест на недействительность (объект obj)

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

test((10 % 2 == 0) ? новый объект() : "stringObj" );

test((10 % 2 == 0) ? новый объект() : null );

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

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

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

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

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

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

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

person Mukesh Singh Rathaur    schedule 20.02.2012

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

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

а также

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

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

person Alberto Gutierrez    schedule 20.02.2012
comment
Я думаю, что вы упустили его точку зрения и сами делаете неправильное предположение. Второй вызывает не test(Object), а test(String). - person Don Roby; 20.02.2012

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

person adranale    schedule 20.02.2012

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  
person Julien    schedule 20.02.2012

Вот что говорится в спецификациях языка Java о проблеме.

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

В вашем случае это метод test(String).

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

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

он покажет ошибку компиляции. Метод test (String) неоднозначен для типа OverloadingTest.

Как говорит JLS:

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

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

person fiction    schedule 20.02.2012