Как троичный оператор оценивает результирующий тип данных?
Учитывая этот кусок кода
public class Main {
public static void main(String[] args) {
foo(1);
foo("1");
foo(true?1:"1");
foo(false?1:"1");
}
static void foo(int i){System.out.println("int");}
static void foo(String s){System.out.println("String");}
static void foo(Object o){System.out.println("Object");}
}
Это вывод, который я получаю:
ИНТ строка объект объект
Я не могу понять, почему в последних двух случаях foo(Object o)
вызывается, а не foo(int i)
а также foo(String s)
, Разве тип возвращаемого значения для троичного выражения не оценивается во время выполнения?
Редактировать:
Что меня смущало, так это утверждение
System.out.println((y>5) ? 21 : "Zebra");
компилируется потому что (OCA Study Guide - Sybex):
System.out.println() не заботится о том, что операторы имеют совершенно разные типы, потому что он может конвертировать оба в String
Дело в том, что println перегружен, чтобы принимать типы объектов в качестве входных данных. Совершенно вводит в заблуждение, имхо.
3 ответа
Разве тип возвращаемого значения для троичного выражения не оценивается во время выполнения?
Нет, абсолютно нет. Это будет очень противоречить тому, как работает Java, где разрешение перегрузки и т. Д. Всегда выполняется во время компиляции. Что бы вы ожидали, если бы вы не передали результат методу, а попытались присвоить его переменной? Какую переменную вы бы объявили?
Тип выражения определяется правилами JLS 15.25. В обоих ваших случаях третий операнд имеет тип String
, что приводит к тому, что это является условным условным выражением, поэтому применяется таблица 15.25-E с результатом lub(Integer,Object)
, lub
часть ссылается на JLS 4.10.4, что довольно запутанно - тип здесь не совсем такой, как просто Object
, но в большинстве случаев это можно считать таким.
Обе альтернативы тройной должны быть одного типа. Единственный общий тип для Integer и String - это Object, поэтому оба операнда приводятся к Object, а тип троицы определяется во время компиляции как Object.
Затем компилятор статически связывается с методом с параметром Object.
Не имеет значения, что логически результат троичного числа определяется во время компиляции - компилятор не работает таким образом. Он имеет дело с выражениями и типами. Сначала он должен разрешить тип троичного выражения, а не его значение, и для этого он должен сначала найти общий тип для операндов.
Это, я полагаю, в дополнение к другим ответам. (Или, в частности, для тех, кто хочет понять педантичный ответ.)
Результат троичного условия для ссылочных типов заканчивается lub(trueType, falseType)
так что в данном конкретном случае это lub(Integer, String)
, lub
это:
Наименьшая верхняя граница, или "lub", набора ссылочных типов является общим супертипом, который более специфичен, чем любой другой общий супертип [...].
Чтобы понять lub
мы могли бы сделать своего рода "чистую" версию алгоритма для простых типов следующим образом.
Сначала составьте таблицу для каждого типа. В одном столбце запишите иерархию суперкласса, а в другом столбце запишите все реализованные интерфейсы:
+------------------------+------------------------+
| Integer | String |
+------------------------+---------+--------------+
| classes | interfaces | classes | interfaces |
+------------------------+---------+--------------+
| Object | Serializable | Object | Serializable |
| Number | Comparable | String | Comparable |
| Integer | | | CharSequence |
+---------+--------------+---------+--------------+
(Фактический алгоритм объединил бы классы и интерфейсы как просто "супертипы".)
Сейчас:
- вычеркните все суперклассы, начиная снизу, пока не встретите суперкласс, который является обычным явлением.
- вычеркните все интерфейсы, которые не являются общими.
+------------------------+------------------------+
| Integer | String |
+------------------------+---------+--------------+
| classes | interfaces | classes | interfaces |
+------------------------+---------+--------------+
| Object | Serializable | Object | Serializable |
| Number | Comparable | String | Comparable |
| Integer | | | CharSequence |
+---------+--------------+---------+--------------+
lub
теперь это тип пересечения, заданный этим общим суперклассом и общими интерфейсами:
lub(Integer, String) =
Object & Serializable & Comparable
Кроме этого lub
немного сложнее здесь из-за Comparable
, Технически, lub(Integer, String)
дает бесконечный тип, потому что нам нужно сделать lub(Integer, String)
снова (бесконечно) для аргумента типа Comparable
:
lub(Integer, String) =
Object & Serializable & Comparable<? extends lub(Integer, String)>
Тип условного (boolean ? Integer : String)
затем тип, который является результатом применения преобразования захвата к lub(Integer, String)
(Yikes!).
foo(Object)
перегрузка выбрана, потому что это наиболее применимо к этому типу.
Несколько других интересных вещей об этом:
- Если мы добавили перегрузку
foo(Serializable)
, он будет выбран, потому чтоSerializable
более конкретно, чемObject
, - Если мы добавили две перегрузки
foo(Serializable)
а такжеfoo(Comparable<?>)
, мы получим ошибку неоднозначности.