Перегрузка метода и выбор наиболее конкретного типа
Пример кода:
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)
при компиляции? Я хочу знать, вычисляются ли выражения во время компиляции или времени выполнения. Спасибо.
Ответы
Ответ 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 говорит:
Возможно, что никакой метод не является наиболее конкретным, поскольку существуют два или более максимально конкретных методов. В этом случае:
Если все максимально конкретные методы имеют одну и ту же подпись, тогда: Если один из максимально специфических методов не объявлен абстрактным, это является наиболее конкретным методом. В противном случае все максимально конкретные методы обязательно объявляются абстрактными. Наиболее конкретный метод выбранных произвольно среди максимально специфических методов. Однако считается, что наиболее конкретный метод выбрал проверенное исключение, если и только если это исключение объявлено в предложениях бросков каждого из максимально конкретные методы. В противном случае мы говорим, что метод вызов является неоднозначным, и возникает ошибка времени компиляции.