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
.
Надеюсь, что это поможет.