Разработка ответа, который дал Майкл Берри.
Dog d = (Dog)Animal; //Compiles but fails at runtime
Здесь вы говорите компилятору «Поверь мне. Я знаю, что d
это действительно относится к Dog
объекту», хотя это не так.
Помните, что компилятор вынужден доверять нам, когда мы делаем уныние .
Компилятор знает только об объявленном ссылочном типе. JVM во время выполнения знает, что на самом деле представляет собой объект.
Поэтому, когда JVM во время выполнения выясняет, что Dog d
фактически ссылается на объект, Animal
а не на Dog
объект, он говорит. Эй ... ты солгал компилятору и бросил большой жир ClassCastException
.
Так что, если вы подавлены, вы должны использовать instanceof
тест, чтобы не облажаться.
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
Теперь возникает вопрос. Почему, черт возьми, компилятор допускает уныние, когда в конечном итоге он собирается броситьjava.lang.ClassCastException
?
Ответ заключается в том, что все, что может сделать компилятор, - это убедиться, что оба типа находятся в одном и том же дереве наследования, поэтому в зависимости от того, какой код мог появиться перед понижением, возможно, он animal
имеет тип dog
.
Компилятор должен разрешать вещи, которые могут работать во время выполнения.
Рассмотрим следующий фрагмент кода:
public static void main(String[] args)
{
Dog d = getMeAnAnimal();// ERROR: Type mismatch: cannot convert Animal to Dog
Dog d = (Dog)getMeAnAnimal(); // Downcast works fine. No ClassCastException :)
d.eat();
}
private static Animal getMeAnAnimal()
{
Animal animal = new Dog();
return animal;
}
Однако, если компилятор уверен, что приведение не будет работать, компиляция не удастся. IE если вы пытаетесь привести объекты в разные иерархии наследования
String s = (String)d; // ERROR : cannot cast for Dog to String
В отличие от downcasting, upcasting работает неявно, потому что, когда вы upcast, вы неявно ограничиваете количество методов, которые вы можете вызывать, в отличие от downcasting, что подразумевает, что позже вы можете захотеть вызвать более конкретный метод.
Dog d = new Dog();
Animal animal1 = d; // Works fine with no explicit cast
Animal animal2 = (Animal) d; // Works fine with n explicit cast
Оба вышеперечисленных upcast будут отлично работать без каких-либо исключений, потому что Dog IS-A Animal может делать то же самое, что и Animal. Но это не правда, наоборот.