Na classe Jogo
apresentada no início deste capítulo, deixámos o método terminou
em aberto (mais adiante lá voltaremos), ou seja, considerámos que não há nenhum critério de terminação que seja comum à maior parte dos jogos.
O método vencedores
, pelo contrário, foi implementado de modo a devolver o(s) jogador(s) com melhor pontuação. Ou seja, considerámos que este critério – melhor pontuação – é suficientemente geral para se aplicar à maior parte dos jogos. Há vários tipos de jogos em que o vencedor é o que tem maior pontuação, nomeadamente os jogos que terminam ao fim de um dado número de jogadas, ou ao fim de determinado tempo, etc.
Por acaso (ou de propósito, na verdade) não é isso que acontece no JogoComObjetivo
. Neste tipo de jogo queremos que seja vencedor todo e qualquer jogador que tenha alcançado a pontuação objetivo.
Mas se já temos uma implementação do método vencedores
herdada da classe Jogo
, como vamos fazer isso?
vencedores
na subclasse, ou seja, vamos dar-lhe uma nova implementação;vencedores
for invocado sobre um objeto do tipo JogoComObjetivo
, é esta nova implementação que é executada e não a que está definida na classe Jogo
;
Uma primeira tentativa (que não está correta):
Perguntar a cada jogador qual a sua pontuação e compará-la com o objetivo:
x/**
* Vencedor(es) deste jogo
* @return O nome do(s) jogador(es) que atingiu(ram) o objetivo.
* @requires this.quantosEmJogo() > 0 && this.terminou()
*/
@Override
public List<String> vencedores () {
List<String> result = new ArrayList<String>();
for (Jogador jog : this.jogadores){
if(jog.pontuacao() >= this.objetivo)
result.add(jog.nome());
}
return result;
}
Qual é o problema com esta solução?
jogadores
foi definido como private
na classe Jogo
Segunda tentativa (agora sim!):
Repare que na classe Jogo
temos um método que nos pode ser muito útil – o método pontuacaoSuperiorA
– que, dado um inteiro, devolve os nomes dos jogadores cuja pontuação é superior a esse inteiro.
Então, podemos usar este método para obter os nomes dos nossos vencedores! Basta invocá-lo, a partir do método vencedores
, usando o valor da pontuação objetivo -1 como limite inferior:
x/**
* Vencedor(es) deste jogo
* @return O nome do(s) jogador(es) que atingiu(ram) o objetivo.
* @requires this.quantosEmJogo() > 0 && this.terminou()
*/
@Override
public List<String> vencedores () {
return this.pontuacaoSuperiorA(this.objetivo - 1);
}
Vamos agora considerar este main
:
xpublic static void main (String [] args) {
JogoComObjetivo meuJogo = new JogoComObjetivo("Spiral",30);
System.out.println("O objetivo eh " + meuJogo.pontuacaoObjetivo());
meuJogo.juntarJogador("Maria");
meuJogo.juntarJogador("Pedro");
meuJogo.registarPontosJogada("Maria", 13);
meuJogo.registarPontosJogada("Pedro", 25);
meuJogo.registarPontosJogada("Maria", 17);
meuJogo.registarPontosJogada("Pedro", 9);
if(meuJogo.terminou()) {
System.out.println("Os vencedores sao:");
List<String> venceram = meuJogo.vencedores();
for (String jog : venceram) {
System.out.println(jog + " com " + meuJogo.pontuacao(jog)); }
} else {
System.out.println("Ainda nao terminou");
}
}
Ao executá-lo, iremos obter o seguinte output:
xxxxxxxxxx
O objetivo eh 30
Os vencedores sao:
Maria com 30
Pedro com 34
Se tivesse sido executada a versão de vencedores
implementada na classe Jogo
o resultado daria diferente – só o Pedro seria vencedor.
Regras na redefinição de métodos em subclasses:
Seja m
um método definido numa classe A
. Seja B
uma subclasse de A
. Seguem-se algumas restrições à redefinição de m
em B
:
m
em B
deve ser exatamente a mesma – em número, ordem e tipos de parâmetros – que a de m
em A
; m
em B
deve ser o mesmo que, ou um subtipo de, o tipo de retorno de m
em A
;m
em B
não pode ser mais restritivo que em A
– por exemplo, se m
em A
é declarado public
, então o nível de acessibililidade de m
em B
não poderá ser nenhum dos outros (private
, protected
ou package);m
em A
é declarado private
, então não pode ser redefinido; se m
em A
tiver um nível de acessibilidade package e a classe B
pertencer a um pacote diferente do de A
, então m
não pode ser redefinido pois não é sequer herdado; m
for declarado como final
em A
, não pode ser redefinido em B
;m
for declarado como static
em A
, não pode ser redefinido em B
mas pode ser re-declarado;private
nem final
;final
declarados public
ou protected
.Repare na anotação @Override
na linha imediatamente acima da assinatura do método vencedores
: esta anotação informa o compilador que a intenção é que aquele método redefina o correspondente método na superclasse.
Embora não seja obrigatória, esta anotação é extremamente útil. Se por alguma razão o método correspondente na superclasse muda – o nome, a lista de parâmetros, etc –, deixa de haver redefinição. Nesse caso o compilador avisa-nos que a subclasse está ilegal pois aquele método devia ser redefinição de algum existente na superclasse e não é o que acontece.
A classe JogoComObjetivo
completa:
x/**
* Representa jogos com o objetivo de alcancar uma dada pontuacao
* @author Isabel Nunes
*/
public class JogoComObjetivo extends Jogo {
// Pontuacao objetivo
private int objetivo;
/**
* Construir um jogo.
* @param designacao A designacao do jogo.
* @param objetivo A pontuacao com que se ganha este jogo
* @requires designacao != null
*/
public JogoComObjetivo (String designacao, int objetivo) {
super(designacao);
this.objetivo = objetivo;
}
/**
* A pontuacao objetivo definida para este jogo.
*/
public int pontuacaoObjetivo () {
return this.objetivo;
}
/**
* Este jogo jah terminou?
*/
@Override
public boolean terminou() {
return this.maiorPontuacao() >= this.objetivo;
}
/**
* Vencedor(es) deste jogo
* @return O nome do(s) jogador(es) que atingiu(ram) o objetivo.
* @requires this.quantosEmJogo() > 0 && this.terminou()
*/
@Override
public List<String> vencedores () {
return this.pontuacaoSuperiorA(this.objetivo - 1);
}
}
Anterior: 14.5. Invocação de métodos herdados
Seguinte: 14.7. Outra subclasse de Jogo