Ошибка компиляции ClassCastException и "can not cast"
Изучение моего экзамена OCA Java SE 7 Programmer I, так что вопрос новичков. У меня есть примерный вопрос, который я не понимаю. Следующий код компилируется, но во время выполнения предоставляет ClassCastException:
interface Roamable {
}
class Phone {
}
public class Tablet extends Phone implements Roamable {
public static void main(String... args) {
Roamable var = (Roamable) new Phone();
}
}
Когда я меняю Roamable var = (Roamable) new Phone();
на Roamable var = (Roamable) new String();
, сразу получаю ошибку компиляции.
Два вопроса:
- Почему код выше компилируется вообще? Телефон кажется несвязанным с Roamable для меня?
- Почему код компилируется с помощью
new Phone()
, но не компилируется ли он с new String()
?
Ответы
Ответ 1
Почему код выше компилируется вообще? Телефон кажется не связанным с Переместим ко мне?
да, поскольку Roamable
- это интерфейс, который может вызвать исключение во время выполнения, но не исключение времени компиляции, потому что даже если Phone
не реализует Roamable
, подкласс Phone
может, следовательно, компилятор не имеет возможности узнать об этом, но во время работы.
Он уже определен в спецификации языка Java. Посмотрите мой ответ здесь.
Почему код компилируется с новым Phone(), но не компилируется с новой строкой()?
Потому что class String
объявлен как public final class
в пакете java.lang
. Как указано в разделе jls 8.1.1.2 final class
: класс, объявленный как final
, не может быть расширен и, следовательно, не будет иметь никакого подкласса. Итак, компилятор уже знает, что String
не может быть расширен: следовательно, невозможно реализовать существование подкласса для реализации интерфейса Roamable
Изменить: (С ответом на ваш комментарий ниже)
Предположим, что B
является подклассом A
, который реализует интерфейс T
.
Теперь инструкция:
T t = (T)new A();
по существу такой же, как:
A aObj = new A() ;
T t = (T)aObj ; // a run-time exception happen
прежде чем приступать к завершению, сделаем то же самое с объектом B
:
A aObj = new B();
T t = (T)aObj; // no exception happen.
поэтому настоящая причина с суперклассом и подклассом здесь является ссылкой. Класс aObj
в этом втором примере кода также является экземпляром класса A
, но он также является экземпляром класса B
, который реализовал T
.
Ответ 2
Строка является окончательной, поэтому ее нельзя отнести к Roamable.
Ответ 3
Код компилируется, потому что компилятор позволяет вам использовать что-либо. Это явное действие программиста, и компилятор предполагает, что вы оценили риски и приняли соответствующие меры предосторожности.
String
не является экземпляром Roamable
, поэтому вы не можете назначить экземпляр String
в ссылку Roamable
. И это может быть определено во время компиляции, поэтому он терпит неудачу.
Ответ 4
new Phone()
evals в класс Phone
, который может быть расширением Phone
, реализующим Roamable
(насколько это касается компилятора).
Если вы создали класс a final
(например, String), вы получите ошибку компилятора.
Ответ 5
Хороший вопрос. new Phone()
определенно не является подклассом Phone
. Однако эта информация теряется, javac видит там тип Phone
, а не точный тип Phone
.
Соответствующая спецификация: http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.5.1
Если S - конечный класс (§8.1.1), то S должен реализовать T,
Спецификация может быть изменена с помощью
Если выражение является выражением создания экземпляра класса (§15.9), тип выражения должен быть подтипом T.
Ответ 6
Сначала прочитайте о Явное и неявное литье типов по java.
От этого пользователя отвечает за явное литье при сужении отношения к объекту, чтобы сказать, что пользователь знает и отлично с ними теряют некоторую точность из-за этого. Однако все же компилятор может обнаружить некоторое явное неправильное литье и бросить CE: Type mismatch error
в какой-то момент. Помимо этого, для работы с ним требуется время выполнения ClassCastException
.
Компилятор может обнаружить следующие случаи явного литья.
class A {}
class B {}
A a = (A) new B(); //CE: Type mismatch: cannot convert from B to A
B b = (B) new A(); //compilation error (CE)
interface I {}
final class A {}
I i = (I) new A(); //compilation error
Компилятор не может обнаружить следующие случаи явного литья.
class A {}
class B extends A {}
A a = (A) new B(); //Fine
B b = (B) new A(); //Runtime error; because compile time;
//compiler wont be able to tell the reference is instance of A or B.
//means this is something like below. <BR>
B b = (B) (A) new A();
Любой объект может быть кастом для любого интерфейса без ошибок компиляции.
interface I {}
class A {}
class B extends A implements I{}
I i = (I) new A(); //Runtime error
I i = (I) new B(); //Fine
Почему эта компиляция?
Ссылка на интерфейсы может использовать касты для любого объекта без ошибки компиляции.
interface I {}
class A implements I {}
class B {}
B b = (B) getI(); //Runtime error
OR
B b = (B)(I)new A(); //Runtime error
public I getI() {
return new A();
}