Передача в нереализованные интерфейсные компиляции
Я не уверен, что понимаю код в строке 1 ниже?
interface Talkable{ }
class Device{}
class Phone extends Device implements Talkable{}
Talkable d = (Talkable) new Device(); //line 1
Talkable p = new Phone(); //line 2
Я понимаю строку2, поскольку Phone реализует Talkable, но Device and Talkable не связаны между собой, как Line1 может быть законным?
Ответы
Ответ 1
Собственно, в Java вполне допустимо применять один родственный тип к другому (даже если кастинг имеет мало смысла). Во время выполнения вы получите сообщение об ошибке, если типы несовместимы.
Например:
public static void main(String[] args) {
String s = (String) new Object();
System.out.println(s.intern());
}
Компилирует отлично, но дает Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
at Sample.main(Sample.java:5)
во время выполнения
Ответ 2
Причина, по которой компилятор соглашается с этим, объясняется в разделе JLS 5.5.1 (соответствующая часть выделена жирным шрифтом):
Учитывая ссылочный тип времени компиляции S (источник) и тип задания времени компиляции T (target), существует преобразование каста от S до T, если ошибки времени компиляции не происходят из-за следующих правил.
Если S - тип класса:
- Если T - тип класса, то либо | S | <: | T | или | T | <: | S |. В противном случае возникает ошибка времени компиляции.
Кроме того, если существует супертип X из T и супертип Y из S, такой, что оба X и Y являются предсказуемыми различными параметризованными типами (п. 4.5) и что стирания X и Y одинаковы, a ошибка времени компиляции.
-
Если T - тип интерфейса:
-
Если S не является окончательным классом (§8.1.1), то, если существует супертип X из T и супертип Y из S, такой, что и X, и Y доказуемо различны параметризированных типов и что стирания X и Y одинаковы, возникает ошибка времени компиляции.
В противном случае листинг всегда является законным во время компиляции (потому что даже если S не реализует T, может быть подкласс S).
В вашем случае a java.lang.ClassCastException
будет запущен во время выполнения, так как Device
не может быть преобразован в Talkable
. Но до тех пор, пока программа не выполнится, компилятор разрешает трансляцию, потому что может существовать подкласс Device
, который реализует Talkable
.
Ответ 3
Строка 1 будет генерировать исключение во время выполнения. Компилятор не проверяет время компиляции, если приведение может быть выполнено успешно. Вот почему иногда рекомендуется сначала проверить это с помощью оператора instanceof
.
В общем случае вы всегда можете использовать переменную x
типа A
для любого интерфейса C
, потому что может существовать класс B extends A implements C
или класс B implements A, C
То же самое не относится к кастованию переменной x
класса A
для любого класса D
, потому что не может существовать подкласс E extends A, D
, потому что класс не может расширять несколько классов.
Ответ 4
Практически в первой строке код явно "сообщает" компилятору, что объект с правой стороны является объектом типа с левой стороны (это называется type casting
). Это справедливо в отношении любых двух типов Java во время компиляции, но если типы не связаны друг с другом, возникает ошибка времени выполнения (будет отклонено Exception
).
В строке 2 выполняется то, что сделано как во время компиляции, так и во время выполнения, потому что Phone
имеет любой тип, который он расширяет и/или реализует, поэтому практически, Телефон является Talkable
(и a Device
),.