Alguém poderia me dar um toque no seguinte: Conversão de objetos entre referências de superclasse e subclasse. Gostaria de mais matérial sobre o assunto. Alguma coisa em mente ?
E uma perguntinha: Finalmente qual é a finalidade de poder usar uma referência de subclasse com um objeto de superclasse ?
Voce pode converter tipos da subclasse para a superclasse, e nunca o contrario:
class Pai {}
class Filho extends Pai {}
..
Filho f = new Filho();
Pai p = f; // ok
f = p// errado
O motivo eh simples: a tua classe filha sabe todos os metodos que a classe pai tem, ja que por defaul ela herda tudo de protected para cima. Agora, a tua classe pai nao tem como saber os metodos que a classe filha tem. Como quando voce faz
Pai p = new Filho();
voce estra criando um objeto do tipo Pai, que aponta para uma referencia do tipo Filho. Como Filho EH um Pai, nao ha problemas em usar ao chamar os metodos, ja que a vm sabe quais os metodos Pai tem ( claro que na hora de invocar tais metodos, a verso do mesmo - caso exista - da classe Filho eh chamada ).
Agora, nao eh possivel fazer
Filho f = new Pai();
pelo fato que voce tem um objeto do tipo Filho que aponta para uma referencia do tipo Pai. Se, nesse caso, voce tentasse chamar um metodo que soh existe em Filho, iria dar pau, pois a tua referencia eh para uma Pai, onde o metodo nao existe…
Cara, isso é MUITO útil…em projeto orientado a objetos, herança/polimorfismo faz uma parte importantíssima…quando você precisa, por exemplo, representar um comportamento genérico que tem várias implementações diferentes, fica fácil colocar uma superclasse com a “interface” (os métodos) genérica e colocar cada implementação específica como uma subclasse. Dessa forma, a parte do programa que vai utilizar esse comportamento não precisa saber qual implementação está sendo usada, apenas como usá-la (o que fica definido na superclasse). Outra vantagem legal que surge disso é a manutenção…alterar, adicionar ou retirar comportamentos específicos diz respeito somente às subclasses que os implementam, e quem usa esse comportamento não precisará ser alterado.
Um exemplo legal de trabalho que eu tive que fazer pra faculdade pra entender a necessidade disso foi implementar um interpretador para uma linguagem que o professor criou: nós tínhamos uma lista de Comando, e essa classe tinha um método abstrato executa. Existem vários comandos na linguagem, mas todos eles são executados da mesma maneira:
Vector comandos = new Vector(); // lista de comandos
/* A lista de comandos é preenchida através da leitura e decodificação
do programa
...
*/
for (int i=0 ; i<comandos.size() ; i++) {
((Comando)comandos.elementAt(i)).executa();
}
Como eu tinha dito antes, cada comando tem sua implementação específica, como ComandoIf, ComandoFor, ComandoWriteln, etc…mas todos eles são executados da mesma forma, e o interpretador, ao executá-los, não precisa nem saber qual comando é.