1) La verifica di correttezza avviene a tempo di compilazione, la scelta sarà fatta a tempo di esecuzione. A tempo di compilazione non è possibile sapere a priori quale tipo di oggetto verrà effettivamente istanziato (anche se in quel caso è possibile prevedere un'ottimizzazione, dato che l'oggetto è chiaramente di tipo Classe2).
2) Un downcast è un cast da classe derivata verso classe base. Generalmente una cosa del genere è del tutto superflua. Il contrario, l'upcast, invece è molto utile quando viene utilizzata l'ereditarietà per generalizzare un metodo. Ad esempio, potrei avere un metodo che accetta oggetti di tipo Figura, al cui interno dovrò discriminare sul tipo effettivo per avere accesso a metodi particolari di ciascuna classe.
Vediamo un piccolo esempio. Qui suppongo di avere una classe Figura che espone i metodi "calcolaArea()" (virtuale, che verrà implementato da ciascuna sottoclasse concreta) e "getTipo()" che mi dice che tipo di figura è.
codice:
public void mostraDettagliArea(Figura f) {
System.out.println("La figura è un " + f.getTipo());
System.out.println("L'area è: " + f.calcolaArea());
if (f instanceof Cerchio) {
System.out.println("Il centro del cerchio è: " + ((Cerchio) f).getCentro());
}
}
La classe Cerchio mette a disposizione un metodo chiamato "getCentro()" che mi restituisce una stringa con le coordinate del cerchio in un ipotetico piano cartesiano... In questo caso sono obbligato a effettuare un upcast, perchè non tutte le figure hanno un centro e non tutte le figure, quindi, hanno un metodo "getCentro()".
Ciao.