Java-литье в интерфейсах

Может кто-нибудь, пожалуйста, объясните мне, как компилятор не жалуется на первое кастинг, но жалуется во втором?

interface I1 { }
interface I2 { }
class C1 implements I1 { }
class C2 implements I2 { }

public class Test{
     public static void main(){
        C1 o1 = new C1();
        C2 o2 = new C2();
        Integer o3 = new Integer(4);

        I2 x = (I2)o1; //compiler does not complain
        I2 y = (I2)o3; //compiler complains here !!
     }
}

Ответ 1

При нажатии o1 и o3 на (I2) вы сообщаете компилятору, что класс объекта фактически является подклассом его объявленного типа и что этот подкласс реализует I2.

Класс Integer final, поэтому o3 не может быть экземпляром подкласса Integer: компилятор знает, что вы лжете. C1 однако не является окончательным, поэтому o1 может быть экземпляром подтипа C1, который реализует I2.

Если вы сделаете C1 final, компилятор тоже будет жаловаться:

interface I1 { }
interface I2 { }
final class C1 implements I1 { }
class C2 implements I2 { }

public class Test{
     public static void main(){
        C1 o1 = new C1();
        C2 o2 = new C2();
        Integer o3 = new Integer(4);

        I2 y = (I2)o3; //compiler complains here !!
        I2 x = (I2)o1; //compiler complains too
     }
}

Ответ 2

Согласно JLS глава 5

5.5.1. Тип ссылочного литья

Учитывая ссылочный тип времени компиляции S (источник) и ссылочный тип времени компиляции T (target), преобразование каста существует от S до T, если ошибки времени компиляции не происходят из-за следующих правил. Если T - тип интерфейса:

Если S не является конечным классом (§8.1.1), то, если существует супертип X из T и супертип Y из S, такой, что оба X и Y являются предсказуемыми различными параметризованными типами и что стирания X и Y одинаковы, возникает ошибка времени компиляции.

В противном случае листинг всегда является законным во время компиляции (потому что даже если S не реализует T, может быть подкласс S).

Если S - конечный класс (§8.1.1), то S должен реализовать T, или возникает ошибка времени компиляции.

Ответ 3

Это потому, что класс Integer является окончательным, а C1 - нет. Таким образом, объект Integer не может реализовать I2, тогда как объект C1 может быть, если он является экземпляром подкласса C1, который реализует I2.

Ответ 4

В соответствии с JLS 5.5.1 - Листинг ссылочного типа, применяются правила (ы):

  • Если T - тип класса, то либо | S | <: | T | или | T | <: | S |. В противном случае возникает ошибка времени компиляции.

    I2 y = (I2)o3; //compiler complains here !!

В этом случае Integer и I2 никак не связаны , поэтому возникает ошибка времени компиляции. Кроме того, поскольку Integer - final, между Integer и I2 нет отношения.

I2 и I1 могут быть связаны из-за того, что оба являются интерфейсом маркера (контракт отсутствует).

Что касается скомпилированного кода, это правило следует:

  • Если S не является окончательным классом (§8.1.1), то, если существует супертип X из T и супертип Y из S, такой, что оба X и Y являются предсказуемыми различными параметризованными типами, и что стирания X и Y одинаковы, возникает ошибка времени компиляции.

S - o1, а T - I2.

Надеюсь, что это поможет.