Também seria bom poder guardar numa mesma coleção, por exemplo um array, jogos de vários tipos e invocar sobre esses jogos os métodos que estão acessíveis a todos, ou seja, os métodos definidos na classe Jogo
:
xpublic class ExemploPolimorfismoArray {
public static void main(String[] args) {
Jogo[] meusJogos = new Jogo[2];
meusJogos[0] = new JogoComObjetivo("Spiral", 25);
meusJogos[1] =
new JogoNumeroExatoJogadas("Harvest Kingdom",1);
meusJogos[0].juntarJogador("Maria");
meusJogos[0].juntarJogador("Pedro");
meusJogos[1].juntarJogador("John Snow");
meusJogos[1].juntarJogador("Daenerys Targaryen");
meusJogos[0].registarPontosJogada("Maria", 11);
meusJogos[0].registarPontosJogada("Pedro", 26);
meusJogos[1].registarPontosJogada("John Snow", 10);
meusJogos[1].registarPontosJogada("Daenerys Targaryen", 10);
for(int i = 0 ; i < meusJogos.length ; i++) {
List<String> venceram = meusJogos[i].vencedores();
for (String jog : venceram) {
System.out.println(jog);
}
}
}
}
O output deste programa é:
xxxxxxxxxx
Pedro
John Snow
Daenerys Targaryen
O array meusJogos
é polimórfico pois contém elementos de diferentes tipos, embora ambos subtipos de Jogo
.
Durante a compilação, como já sabemos, todas as atribuições e passagens de parâmetro são verificadas pelo compilador relativamente à correção dos tipos.
As atribuições
xmeusJogos[0] = new JogoComObjetivo("Spiral", 25);
meusJogos[1] = new JogoNumeroExatoJogadas("Harvest Kingdom",1);
estão corretas do ponto de vista das regras que o compilador aplica (relembrar secção anterior), pois atribuem a cada uma das duas variáveis do tipo Jogo
– meusJogos[0]
e meusJogos[1]
– valores de um subtipo de Jogo
.
De igual modo, as invocações de métodos também são verificadas relativamente à correção dos tipos.
A regra que temos usado:
- Os métodos invocados sobre um objeto têm que estar definidos na classe de que esse objeto é instância.
tem que ser refinada a partir do momento em que temos variáveis polimórficas.
Nestes casos podemos distinguir entre:
- o tipo da variável ou parâmetro cujo valor é uma referência a um objeto – tipo declarado;
- o tipo do objeto que é referenciado por essa variável ou parâmetro, ou seja, a classe de que é instância – tipo concreto.
O compilador permite a invocação de um método m
sobre um objeto o
:
- se
m
está acessível no tipo declarado deo
.
Logo, todas as invocações do programa ExemploPolimorfismoArray
estão corretas, pois todos os métodos invocados sobre meusJogos[0]
e meusJogos[1]
são métodos acessíveis no tipo Jogo
, que é precisamente o tipo declarado dessas duas variáveis.
As seguintes invocações, pelo contrário, seriam rejeitadas pelo compilador:
xmeusJogos[0].pontuacaoObjetivo();
meusJogos[1].jogadasJahFeitas();
Embora o tipo concreto de meusJogos[0]
seja JogoComObjetivo
, que define o método pontuacaoObjetivo()
, no entanto esta invocação está incorreta do ponto de vista dos tipos, pois o tipo de meusJogos[0]
é Jogo
. O mesmo se passa com a segunda instrução, desta vez em relação ao tipo JogoNumeroExatoJogadas
.
De igual modo, no exemplo da secção anterior, em nenhum dos métodos imprimeVencedores(Jogo j)
e imprimeDetalhes(Jogo j)
, se poderiam invocar, sobre o parâmetro j
, métodos que não estivessem definidos em Jogo
.
Anterior: 15.1. Polimorfismo
Seguinte: 15.3. Ligação dinâmica