Разработка ответа, который дал Майкл Берри.
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. Но это не правда, наоборот.