Brincando com Generics: o BizarreGenericDao

Conversando com o Orseni Campos, ele me contou de uma sacada muito interessante que teve juntamente com seu colega Alexandre Bitencourt para resolver um clássico problema do generics: em tempo de execução você não consegue descobrir o tipo parametrizado que foi passado como argumento.

Em outras palavras, repare no código do Dao genérico que foi discutido aqui (estou pulando a interface por questão de simplicidade):

public class Dao<T> {
  // ...
  public Dao(Session session, Class<T> persistentClass) {
    this.session = session;
    this.persistentClass = persistentClass;
  }
}

Aí, quando vamos criar um novo dao genérico:

Dao<Livro> dao = new Dao<Livro>(session, Livro.class);

Parece estranho ter de passar a Class que representa o livro, se eu já estou passando essa informação através do tipo parametrizado. Seria interessante fazer algo como:

public class Dao<T> {
  // ...
  public Dao(Session session) {
    this.session = session;
    this.persistentClass = T.class;
  }
}

Isso não compila, essa informação (sobre quem é T dentro de Dao) não é armazenada em um .class. O motivo é simples. Só existe uma classe Dao, não importa como ela foi instânciada. Em outras palavras:

(new Dao<Autor>(session, Autor.class)).getClass() ==
   (new Dao<Livro>(session, Livro.class)).getClass()

retorna true. Então o problema é o seguinte: existe apenas uma classe, não importa quantas instâncias de tipo parametrizado diferentes você criar. Não faria sentido algum ter um método como clazz.getTypeArguments() que te devolvesse uma array com os tipos parametrizados que tivessem sido usados na instanciação, já que só existe uma classe para todas as instâncias de diferentes tipos parametrizados. Isso se deve a erasure: em tempo de execução só sabemos que T pode ser um filha de Object, nesse caso.

Mas e se existisse mais de uma classe?

class AutorDao extends Dao<Autor> {
}
class LivroDao extends Dao<Livro> {
}

Obviamente (new LivroDao()).getClass() == (new AutorDao()).getClass() retorna false. Mais que isso, agora você consegue buscar por reflection qual é o parâmetro que foi passado na hora de estender a classe Dao, já que AutorDao e LivroDao possuem essa informação em seus bytecodes:

Class clazz = (Class<T>) ((ParameterizedType)
getClass().getGenericSuperclass()).getActualTypeArguments()[0];

Então, se colocamos essa linha dentro da nossa classe mãe:

public class Dao<T> {
  // ...
  public Dao(Session session) {
    this.session = session;
    this.persistentClass = (Class<T>) ((ParameterizedType)
      getClass().getGenericSuperclass()).getActualTypeArguments()[0];
  }
}

Agora funciona caso utilizemos uma instância de uma filha de Dao que tenha explicitado quem é T. Bem, eu não gostei dessa solução porque nos obrigava a ter uma classe filha, mesmo que vazia, para cada entidade. Mas o Orseni e o Alexandre não desistiram. A proposta deles foi de declarar Dao como abstrata, e na hora de instanciar:

Dao<Livro> dao = new Dao<Livro>(session){};

Repare o {}! Esse código vai gerar uma classe anônima em tempo de compilação, e essa classe sem nome vai ser filha de Dao<Livro>, e agora podemos pegar essa informação por reflection! Isso sim é gambiarra criatividade :) . Usar em produção? Acho que não seria elegante. Nem eles estão usando, mas foi um bonito desafio.

Fonte: Caelum – Paulo Silveira

Anúncios

About Gustavo Amaro

+ Formado em Tecnologia e Análise de Sistemas + MBA em Desenvolvimento de Aplicações JAVA – SOA

Posted on 21 de Fevereiro de 2013, in Java, Persistência and tagged , , , , , , , , , , , . Bookmark the permalink. Deixe um comentário.

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s

%d bloggers like this: