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 шт):

Автор решения: CrazyElf

var - это синтаксический сахар. По факту компилятор должен завести переменную такого типа, в которую можно поместить и класс A и класс B. Обычно в таких случаях используется прародитель всех объектов Object. Что вы и наблюдаете.

Что тут можно сделать - описать интерфейс, в котором есть randomMethod и унаследовать от него оба ваших класса. А перед вызовом метода преобразовать переменную randomClass к этому интерфейсу. В переменную то интерфейс записать всё-равно не получится, в переменной всё-равно будет Object.

Ну либо сделать базовый класс для ваших объектов с этим методом, и помещать в переменную объект базового класса.

→ Ссылка
Автор решения: Grundy

Тернарный оператор равносилен следующей записи

var randomClass;

if (f) {
    randomClass = new A();
} else {
    randomClass = new B();
}

Здесь видно, что переменной нужно сначала указать тип. Так как типы разные, общий тип для обоих классов будет Object

→ Ссылка
Автор решения: talex

Оператор var v = b ? foo : bar; на самом деле превращается в:

T v;
if(b) {
   v=foo;
} else {
   v=bar;
}

Где T — наиболее специфичный общий тип для foo и bar.

В твоём случае общий тип у A и B это Object.

→ Ссылка
Автор решения: rotabor

Вы всё время путаете процесс компиляции программы и процесс её выполнения. "Вне зависимости от значения f, выведение типов присваивает" - здесь значение анализируется в процессе выполнения, а выведение типов происходит в процессе компиляции.

"То есть в зависимости от f, randomClass должен ссылаться либо на экземпляр класса А, либо на экземпляр класса B." - это относится к периоду выпонения. Но как об этом узнает компилятор?

Остальное объяснено в других ответах.

→ Ссылка