A idéia é pra saber o que compila…
A) p0 = p1, Esta correta pq p0 (ClassA) é um super Tipo de ClassB (p1) … sendo assim é possivel atribuir o valor de p1 a p0, sem precisar de cast
E) p1 = (ClassB)p3, Esta correto pq p3 (ClassA) é um superTipo de ClassB (p1) asssim vc deve fazer uso do cast, para transformar uma ClassA em uma mais especializada ClassB, que esta dentro da mesma arvore, por isso funciona…
F) p2 = (ClassC)p4, Esta correto pq p4 (ClassA) é um superTipo de ClassC (p2) assim vc deve fazer uso do cast, para transformar uma ClassA em uma mais especializada ClassC, que esta dentro da mesma arvore, por isso funciona…
…
B) p1 = p2 … não é possivel atribuir uma ClassC a uma ClassB pois são tipos diferentes
C) p2 = p4 … para atribuir uma ClassA a uma ClassC é preciso realizar um cast, conforme é feito na alternativa E
D) p2 = (ClassC)p1 … ClassB não faz parte da mesma hieraquia de ClassC, portanto é impossivle realizar esse cast, e o compilador não aceitara o código…
…
Os casts em tempo de compilação são testado de acordo com as referencias, ou seja, o compilador testa se é possivel realizar o cast para uma dada referencia…
Em tempo de execução os casts são testados com os valores dentro das variáveis de referencia, caso não seja possivel o cast uma ClassCastException é lançada…
um exemplo disso é que
p1 = (ClassB)p4;
irá compilar, pq a variável p4 (ClassA) pode ser castada em uma p1(ClassB)
porem esse código gera um Exceção ao rodar, pois dentro de p4 existe uma ClassC, e não é possivel tranformar uma ClassC em ClassB para entrar em p1, gerando uma ClassCastException