Перегрузка метода и выбор наиболее конкретного типа - PullRequest
22 голосов
/ 20 февраля 2012

Пример кода:

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

Ответы [ 9 ]

20 голосов
/ 20 февраля 2012

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

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

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

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

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

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

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

2 голосов
/ 20 февраля 2012

JLS 15,25:

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

[...]

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

[...]

Так типа

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

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

2 голосов
/ 20 февраля 2012
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 не имеет значения.

1 голос
/ 20 февраля 2012

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

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

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

test (null);

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

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

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

, который будет возвращать логическое значение "истинное значениеИтак, первое логическое «истинное» значение будет автоматически приведено к объекту класса Boolean Wrapper.Boolean wrapper Object находит наилучшее совпадение с вашим параметром new Object() в троичном операторе.И метод вызывает 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);

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

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

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

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

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

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

1 голос
/ 20 февраля 2012

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

0 голосов
/ 20 февраля 2012

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

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

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

И поэтому вы добавляете ...

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

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

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

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

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

0 голосов
/ 20 февраля 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  
0 голосов
/ 20 февраля 2012

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

.
0 голосов
/ 20 февраля 2012

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

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

и

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

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

...