Transformando objeto de uma superclasse em subclasse

Bom dia à todos

Estou tendo um problema com a implementação de uma herança em uma aplicação. Imaginemos o seguinte exemplo:

[code]
public class Pessoa{
String cpf;
String nome;
}

public class Usuario extends Pessoa {
String username;
String senha;
}[/code]

Meu problema é o seguinte: eu quero criar um objeto Usuario a partir de um objeto Pessoa. Como o java não faz downcasting automático, já tentei fazer um typecast com Usuario, mas dá uma ClassCastException.

Existe alguma forma dessa “conversão” ser feita automaticamente ou eu tenho de implementar, na classe Usuario, algo que receba um objeto do tipo pessoa e o instancie?

bom vamo ver se entendi o que você quer fazer:

No seu caso o java consegue identificar que um usuario é uma pessoa:

Usuario u = new Usuario();
boolean eUmaPessoa =  u instanceof Pessoa;

//eUmaPessoa vai ser true

agora se você quer fazer o contrario não da pois do modo que você esta fazendo uma pessoa nunca vai ser um usuario.

Você teria que fazer Pessoa ser uma interface, e Usuario teria que implementar essa interface.

e dai no momento de instanciar uma pessoa ela poderia ser ou não um usuario

public interface Pessoa{
String cpf;
String nome;
}

public class Usuario implements Pessoa {
String username;
String senha;
}
Pessoa p = new Usuario();
boolean eUmUsuario =  p instanceof Usuario;

//eUmUsuario vai ser true

acho que era isso? ou interpretei errado? (não testei pode ter pequeno erros , Maisculo minusculo, testa ai qualquer coisa avisa)

Cara, imagino quem nao seja possivel…

Imagine pq:

entendendo o seu exmplo,

  • a classe Pessoa é a superclasse, ou seja, classe Pai.
  • classe Usuario é a subclasse, ou seja, classe Filho.

Pelo que entendi, vc está querendo que a classe Filho seja Pai da classe Pai…

  • O Pai sempre será Pai.
  • O Filho sempre será Filho.
  • O Filho poderá tornar-se Pai de outros Filhos.
  • O Filho nunca pode tornar-se Pai do Pai (a não ser no filme Superman, the Returns - hehehe)

Blz?

Abraços

Na verdade não é bem assim…

O que eu realmente quero é por pura preguiça e conveniência…

Imaginem o seguinte, utilizando as classes acima (por preguiça eu não coloquei os getters nem setter, mas vamos imaginar que eles existem)

[code]Pessoa pessoa = new Pessoa();
pessoa.setNome(“José da Silva”);
pessoa.setCpf(“123”);

Usuario user = new Usuário();
user.setNome(pessoa.getNome());
user.setCpf(pessoa.getCpf());
user.setLogin(“zedasilva”);
user.setSenha(“avlisadez”);[/code]

Na verdade, eu quero é mais ou menos isso… mas eu gostaria que, de alguma forma mágica, eu utilizasse o objeto pessoa pra criar uma instância de usuário…

Imaginem o caso, de repente, eu preciso modificar a classe pessoa e inserir mais um atriubuto, por exemplo, rg. Eu teria que refatorar meu código em todo lugar que eu fizesse essa transposição para que o rg fosse preenchido em usuário tb…

Não vejo, conceitualmente, muita barreira pra isso, pois um usuário é uma pessoa… então, deveria ser possível eu criar um a partir do outro…

Porque não assim?

   Usuario user = new Usuario();  
   user.setNome("José da Silva");  
   user.setCpf("123");  
   user.setLogin("zedasilva");  
   user.setSenha("avlisadez");  

É que meu cenário é um pouco diferente…

Estou trabalhando num esquema de autenticação utilizando JAAS que eu desenvolvi. Funcionaria mais ou menos assim

  • Ele faria a autenticação em uma base qualquer e traria os dados da pessoa;
  • caso essa pessoa existisse em minha base de permissões, ele carregaria as permissões e instanciaria um usuário com a pessoa carregada;
  • caso não existisse, ele insere essa pessoa e cria permissões básicas, transformando ela num usuário.

Acontece que a classe usuário é apenas utilizada nesse contexto do login; eu tenho um método que me retorna uma pessoa da base de dados, que eu utilizo para verificar se ele existe ou não, e eu gostaria de reaproveitar esse método. Por isso da necessidade de eu transformar uma pessoa direto em um usuário.

Cara… entendi o que vc quer fazer…

Não tem muita mágica não…

Acho que a maneira mais fácil de vc fazer isso… é utilizar a classe BeanUtils do Commons da Apache.

ex:

...
Usuario usuario = new Usuario();
BeanUtils.copyProperties(usuario,pessoa); //Esse método copia todos atributos(desde que havam getters e setters)

Referência: http://commons.apache.org/beanutils/apidocs/org/apache/commons/beanutils/BeanUtils.html

Você está fazendo alguma salada.

O correto é fazer isto aqui:

public class Pessoa{  
    private String cpf;  
    private String nome;  
    public Pessoa (String cpf_, String nome_) { cpf = cpf_; nome = nome_; } 
    // getters & setters
}  
   
public class Usuario  {  
    private Pessoa pessoa;
    private String username;  
    private String senha;  
    public Usuario (Pessoa pessoa, String username_, String senha_) {  pessoa = pessoa_; username = username_; senha = senha_; }
    // getters & setters 
} 

...

Pessoa p = new Pessoa ("123.456.789-10", "João Não Usuário");
Usuario u = new Usuario (p, "jose.pagador", "abcde");

No seu caso, em vez de fazer Usuario herdar de Pessoa, faça Usuario ter um campo do tipo Pessoa.

Entendi o q vc quis dizer, thingol.

Mas aí não fica meio estranho?
Um usuário é uma pessoa, não contém uma pessoa.
Não sei se seria conceitualmente correto carregar esse objeto dentro dele…

Aham - falha nossa - corrigi meu exemplo de novo…

De qualquer maneira, essa é uma coisa meio chata da linguagem Java: um objeto não pode mudar de classe dinamicamente.
Outro dia alguém perguntou se ele podia promover um Aprendiz para Guerreiro ou outra coisa parecida (em um problema daqueles que temos de resolver, normalmente seria promover um Funcionario para Gerente ou um Gerente para Diretor).
Se você pudesse transformar um objeto de uma superclasse em um objeto de uma subclasse, apenas adicionando os atributos novos, resolveria problemas semelhantes ao seu.

Realmente, é um problema, mas consegui resolver utilizando a solução proposta pelo ignacio83, que é utilizar a classe beanutils pra copiar o objeto.

Obrigado à todos pela ajuda!

[quote=paulodamaso]Entendi o q vc quis dizer, thingol.

Mas aí não fica meio estranho?
Um usuário é uma pessoa, não contém uma pessoa.
Não sei se seria conceitualmente correto carregar esse objeto dentro dele… [/quote]

E…se a gente assumir que o usuário (Usuario) não seja uma pessoa (Pessoa) e sim um papel que a pessoa desempenha; o exemplo do thingol não ficaria bacaninha?

flws

Uma coisa que percebi é que se pudéssemos mudar a classe em tempo de execução isso resolveria alguns problemas de modelagem.

Um dos problemas é o seguinte: digamos que você tenha um objeto do tipo Aprendiz, que foi promovido a Guerreiro. (E tenha a hierarquia Jogador -> Aprendiz -> Guerreiro -> General -> Rei, por exemplo).

Ele tem alguns atributos adicionais (até aí tudo bem), mas o problema é que eu não posso simplesmente pegar o tal objeto do tipo Aprendiz e copiá-lo para um objeto do tipo Guerreiro, senão eu teria de ficar procurando TODAS as referências a ele no programa, para substituí-las por esse novo objeto (ou seja, você deveria poder morrer para ressuscitar , mas o problema é que, devido às referências, você não consegue morrer para poder ser substituído).

Como vocês viram, deve ser melhor separar essa parte do papel que um determinado objeto desempenha, e separá-lo em um atributo. Ou seja, há só uma classe Jogador, que não tem descendentes, mas há várias classes derivadas de Papel, por exemplo PapelAprendiz, PapelGuerreiro, PapelGeneral e PapelRei.
Quando você precisar promover um Jogador de aprendiz para guerreiro, você só altera o atributo Papel, com um objeto da classe adequada (PapelGuerreiro) contendo os atributos necessários para um guerreiro.

Ou seja, o conceito “É UM” da OOP é um pouco vago. Isso porque o verbo “SER” é bastante ambíguo em português.

Muitas vezes, o que ocorre é que um determinado objeto “DESEMPENHA O PAPEL DE” (em vez de “SER UM”); nesse caso, é necessário criar uma classe Papel, e fazer a classe ter um atributo do tipo Papel.

Se algo pode mudar de categoria durante seu tempo de vida (um funcionário ser promovido a gerente, ou ser rebaixado), então você tem esse caso de “desempenhar o papel de”.

Saquei

Sendo aprendiz, não posso saber se é guerreiro ou não; sem guardar uma refencia de aprendiz, o guerreiro seria um guerreiro “do zero”, como se fosse criado naquele momento, com uma referência nova; o correto mesmo é esse guerreiro ter uma referência pra aprendiz, indicando qual aprendiz ele é.

Só que isso detona a herança, não? Digo assim, a herança vira uma coisa redundante; digamos que:

[code]class Pessoa{
String nome;
String cpf
}

class Usuario extends Pessoa{
Pessoa p;
String login;
String senha;
}[/code]

Então, eu poderia fazer

Usuario u= new Usuario(); u.getPessoa().getNome(); u.getNome();

O que me dá redundância e é conceitualmente errado. No caso, eu precisaria de um relacionamento de herança para o tratamento de um usuário como uma pessoa em certos lugares da aplicação.

Agora, uma outra dúvida sobre herança

Vamos imaginar o seguinte código… São métodos bobos, mas só pra exemplificar:

[code]public class Pessoa{
private String nome;
private String cpf;

private boolean validaCpf(){
if (cpf != null) return true
return false;
}

public validaPessoa(){
return validaCpf();
}
}

public class Usuario extends Pessoa{
private String login;
private String senha;

public login();
}[/code]

Agora vem a cruel questão. Por ser uma pessoa, usuário não deveria ter acesso ao método validaCpf()? Certo, eu sei que ele precisaria ser protected para ser lido pelos descendentes da classe… então ele simplesmente deixa de existir quando fazemos a herança?

Chamando o metodo validaPessoa em uma instância da classe Usuario ele acharia o validaCpf?

Caso eu não possa modificar o validaCpf para protected (por exemplo, estou utilizando uma biblioteca do java), como fazer pra conseguir chamar esses métodos sem copiar código de implementação?

Hum… a verificação de acesso (por exemplo, se um método é público, protegido ou privado) é feita só por quem chama o método.

Dentro do método validaPessoa você pode fazer qualquer coisa, inclusive chamar validaCPF (que é acessível dentro da classe Usuário).

O que você não pode fazer, se validaCPF não for pelo menos protected, é copiar a implementação de validaPessoa para a classe Usuário.

Mas eu não consigo usar validaCpf de uma instância de usuário…

já tentei, ele diz que não tem acesso.

Eu precisaria disso, não quero reimplementar esse método

Isso pq validaCPF é PRIVATE.
Se vc quer que as outras instâncias o acessem, terá que ser PROTECTED ou PUBLIC.

Princípio da herança… os objetos filhos só acessam atributos ou metodos da classe Pai se estes forem protected ou public.

Abraços