Algumas linguagens permitem que você altere dinamicamente a classe de um objeto já existente.
Em Java, no entanto, isso infelizmente não existe.
A forma mais estúpida de fazer isso é ter um construtor na classe Guerreiro que recebe um objeto da classe Aprendiz, e copie os campos adequados. O problema, obviamente, é que você vai ter de ficar caçando no seu programa todos os pontos em que esse tal objeto é referenciado, e trocar por uma referência para o novo objeto. Então nem sempre é uma alternativa.
Outra forma é assumir que Guerreiro, Aprendiz etc. são todos Jogadores, e ter apenas objetos da classe Jogador. Entretanto, como você quer ter implementações diferentes para os métodos “lutar”, etc, o que você pode fazer é ter várias classes (HabilidadesGuerreiro, HabilidadesAprendiz etc) que são implementações desses tais métodos. Você então põe um atributo na sua classe Jogador que é uma instância dessa classe Habilidades.
Ao você alterar um jogador de Aprendiz para Guerreiro, você altera só esse atributo.
enum Fase { APRENDIZ, GUERREIRO, ..... };
interface Habilidades {
void lutar (Jogador j);
}
class HabilidadesGuerreiro implements Habilidades {
public void lutar (Jogador j) { .... }
}
class HabilidadesAprendiz implements Habilidades {
public void lutar (Jogador j) { .... }
}
class Jogador {
private Habilidades hab;
public void lutar () { hab.lutar (this); }
...
public void evoluiFase (Fase f) {
switch (f) {
case APRENDIZ: hab = HabilidadesAprendiz.getInstance(); break;
case GUERREIRO: hab = HabilidadesGuerreiro.getInstance(); break;
...
}
}
}
...
Jogador j = new Jogador (Fase.APRENDIZ); // isto faz com que suas habilidades sejam as de um aprendiz
j.lutar(); // j.lutar chama hab.lutar (j); com hab sendo uma instância de HabilidadesAprendiz
j.evoluiFase (Fase.GUERREIRO);
j.lutar(); // j.lutar chama hab.lutar (j); com hab sendo uma instância de HabilidadesGuerreiro
...