Подтвердить что ты не робот

Ошибка компиляции 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()?
4b9b3361

Ответ 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();
}