Java - тернарный оператор и классы
Имеется следующий код:
class Random {
public static void main(String[] args) {
boolean f = true; // false
var randomClass = f ? new A() : new B();
randomClass.randomMethod();
}
}
class A {
public void randomMethod() {
System.out.println("Method in class A");
}
}
class B {
public void randomMethod() {
System.out.println("Method in class B");
}
}
По моему предположению, он должен был в зависимости от f
инициализировать переменную randomClass
либо как ссылочную переменную на экземпляр класса А
, либо на B
. Но на практике, код выше даже не скомпилировался.
Вне зависимости от значения f
, выведение типов присваивает переменной randomClass
ссылку не на экземпляр класса A
или B
, а на экземпляр класса Object
. Из-за того, что в Object
метод randomMethod()
не определен, сам код не компилируется.
Почему так происходит, ведь с точки зрения логики тернарный оператор должен быть равносилен следующей записи:
if (f) {
var randomClass = new A();
} else {
var randomClass = new B();
}
То есть в зависимости от f
, randomClass
должен ссылаться либо на экземпляр класса А
, либо на экземпляр класса B
.
Мне не нужны альтернативные решения того, как можно это реализовать без использования тернарного оператора. Нужно именно объяснение, почему тернарный оператор выдает такой результат.
Ответы (4 шт):
var
- это синтаксический сахар. По факту компилятор должен завести переменную такого типа, в которую можно поместить и класс A
и класс B
. Обычно в таких случаях используется прародитель всех объектов Object
. Что вы и наблюдаете.
Что тут можно сделать - описать интерфейс, в котором есть randomMethod
и унаследовать от него оба ваших класса. А перед вызовом метода преобразовать переменную randomClass
к этому интерфейсу. В переменную то интерфейс записать всё-равно не получится, в переменной всё-равно будет Object
.
Ну либо сделать базовый класс для ваших объектов с этим методом, и помещать в переменную объект базового класса.
Тернарный оператор равносилен следующей записи
var randomClass;
if (f) {
randomClass = new A();
} else {
randomClass = new B();
}
Здесь видно, что переменной нужно сначала указать тип. Так как типы разные, общий тип для обоих классов будет Object
Оператор var v = b ? foo : bar;
на самом деле превращается в:
T v;
if(b) {
v=foo;
} else {
v=bar;
}
Где T
— наиболее специфичный общий тип для foo
и bar
.
В твоём случае общий тип у A
и B
это Object
.
Вы всё время путаете процесс компиляции программы и процесс её выполнения. "Вне зависимости от значения f, выведение типов присваивает" - здесь значение анализируется в процессе выполнения, а выведение типов происходит в процессе компиляции.
"То есть в зависимости от f, randomClass должен ссылаться либо на экземпляр класса А, либо на экземпляр класса B." - это относится к периоду выпонения. Но как об этом узнает компилятор?
Остальное объяснено в других ответах.