Por lo general, el downcasting es lo que haces cuando el conocimiento estáticamente conocido que el compilador tiene sobre el tipo de algo es menos específico de lo que sabes (o al menos esperas).
En situaciones como su ejemplo, el objeto fue creado como un Appley luego ese conocimiento fue desechado almacenando la referencia en una variable de tipo Fruit. Entonces desea utilizar la misma referencia como una Applevez más.
Dado que la información solo se descartó "localmente", seguro, el compilador podría mantener el conocimiento que parentes realmente Apple, a pesar de que su tipo declarado lo es Fruit.
Pero generalmente nadie hace eso. Si desea crear un Appley usarlo como Apple, lo almacena en una Applevariable, no en Fruituna.
Cuando tienes un Fruity quieres usarlo como un Apple, generalmente significa que obtuviste a Fruittravés de algún medio que generalmente podría devolver cualquier tipo de Fruit, pero en este caso sabes que era un Apple. Casi siempre no lo ha construido, sino que le ha pasado algún otro código.
Un ejemplo obvio es si tengo una parseFruitfunción que puede convertir cadenas como "manzana", "naranja", "limón", etc., en la subclase apropiada; en general, todo lo que (y el compilador) podemos saber acerca de esta función es que se vuelve una especie de Fruit, pero si llamo parseFruit("apple")a continuación, que sabemos que va a llamar a una Appley puede ser que desee utilizar Applemétodos, por lo que podría abatido.
Una vez más, un compilador suficientemente inteligente podría resolverlo aquí al incluir el código fuente parseFruit, ya que lo llamo con una constante (a menos que esté en otro módulo y tengamos una compilación separada, como en Java). Pero debería poder ver fácilmente cómo los ejemplos más complicados que involucran información dinámica podrían ser más difíciles (¡o incluso imposibles!) Para que el compilador los verifique.
En el código realista, las descargas se producen cuando el compilador no puede verificar que la descarga sea segura utilizando métodos genéricos, y no en casos tan simples como inmediatamente después de una descarga que arroja la misma información de tipo que estamos tratando de recuperar mediante la descarga.