Este tutorial mostra como utilizar Generics em Java para nos auxiliar na criação de classes robustas e com poder de abstração imenso, ou seja, você será capaz de criar uma classe poderosa o bastante para comportar-se das mais diversas formas possíveis dependendo da situação que você precisar, através do conceito de reusabilidade.

É muito comum em qualquer sistema o uso de operações de CRUD (Inserção, Deleção, Remoção e Pesquisa), mas em certo ponto isso torna um código repetitivo e a produtividade de desenvolvimento do sistema cai muito. Tais problemas poderiam ser solucionados apenas criando uma classe Abstrata com Generics que faça todas as operações considerando o tipo do bean em questão.

Criando uma Classe Crud com Generics

Vamos começar utilizando o poder do Generics com uma classe de CRUD, ou seja, que tenha todas as operações de manutenção a determinado objeto. Nosso objetivo é deixar a nossa classe o mais complexa (em termos de recurso e não de dificuldade) possível para atender as mais diversas possibilidades, sendo assim, apenas em casos muito específicos teremos que criar classes a parte, ou seja, que não estendam nossa classe abstrata. Observe a Listagem 1.

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

/*
* Nossa classe CrudImpl será responsável por todas as operações 
* de CRUD do sistema, apenas em alguns casos onde alguma 
* operação de CRUD Deverá ser mais especializada que os métodos 
* deverão ser sobreescritos (override).
* */
public abstract class AbstractCrud<Bean> {

     /*
      * Nos permite retornar novas instancias do nosso objeto
      */
     protected Bean criadorBean;
     protected BasicDAOImpl basicDAO;

     public Bean save(Bean bean) {
      try {
       if (bean == null) {
        throw new RuntimeException("O Objeto não pode ser nulo");
       }
       // Se o seu ID for diferente de NULO quer dizer 
       // que ele já foi salvo apenas ignoramos um novo "save"
       if (bean.getId() != null) {
         return bean;
       }
       return (Bean) getDao().save(bean);

      } catch (Exception e) {
        e.printStackTrace();
        return null;
      }

   }

   public Bean update(Bean bean) {
    try {
     if (bean == null) {
       throw new RuntimeException("O Objeto não pode ser nulo");
     }
     // Se o seu ID for NULO, chamamos o save em vez do update
     if (bean.getId() == null) {
      return save(bean);
     }
     return (Bean) getDao().update(bean);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  public void remove(Bean bean) {
    try {
     if (bean == null) {
       throw new RuntimeException("O Objeto não pode ser nulo");
     }
     // Se o seu ID for NULO, significa que o objeto não foi salvo, 
     // então não podemos remove-lo
     if (bean.getId() == null) {
       throw new RuntimeException(
         "Você não pode remover um objeto que ainda não foi salvo");
     }

       getDao().remove(bean);
    } catch (Exception e) {
       
       e.printStackTrace();
    }
  }
  public List<Bean> findAll(Bean bean) {
    try {
      if (bean == null) {
       throw new RuntimeException("O Objeto não pode ser nulo");
      }
      return (List<Bean>) getDao().findAll(bean);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }

  }

  /*
   * Retorna uma instancia do Bean, ou seja, como se estivessemos 
   * executando o"new MinhaClass();", mas faremos isso com Generics.
   */
  public Bean getInstanceOfbean() {
   Type type = getClass().getGenericSuperclass();
   ParameterizedType paramType = (ParameterizedType) type;
   try {
    return ((Class<Bean>) paramType.getActualTypeArguments()
    [0]).newInstance();
   } catch (InstantiationException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
     return null;
   } catch (IllegalAccessException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
     return null;
   }
 }

 public BasicDAOImpl getDao() {
   if (basicDAO == null)
     basicDAO = new BasicDAOImpl();
   return basicDAO;
  
 }

}
Listagem 1. Classe Abstrata implementando operações de CRUD

O importante na Listagem 1 é perceber que podemos usar dos mais diversos recursos para tornar nossa classe abstrata e poderosa o suficiente para que adapte-se ao meio, ou seja, dependendo da regra em que estamos trabalhando, nossa classe será capaz de adaptar-se a isto, evitando a reescrita de código.

É verdade que a construção dessa classe pode levar um pouco de tempo e ser demasiadamente trabalhosa, mas vale ressaltar que a criação dessas classes abstratas poderosas, aumentam a produtividade do desenvolvimento de forma escalar, levando ainda em consideração que você pode reutilizar a mesma classe nos mais diversos sistema, ou seja, o trabalho árduo será feito apenas uma vez.

Temos então nossa classe abstrata criada, e pelo fato de ser abstrata, não pode ser implementada. Precisamos então criar uma classe que a implemente.

Para nosso exemplo criaremos a classe PessoaCrudImpl (Listagem 2) que estenderá nossa classe abstrata implementando os métodos necessários e abstraindo todos os recursos contidos na mesma.

public class PessoaCrudImpl extends AbstractCrud<Pessoa> {   
}
Listagem 2. PessoaCrudImpl estendendo da classe AbstractCrud

Nossa classe PessoaCrudImpl tem recurso suficiente para realizar todas as operações de CRUD necessárias, pois através do “AbstractCrud<Pessoa>” dizemos a nossa classe abstrata que o tipo de objeto que estamos trabalhando é do tipo Pessoa e este tipo é atribuído diretamente ao parâmetro “Bean” especificado no “AbstractCrud<Bean>” da nossa classe abstrata.

Veja o quão simples se tornar trabalhar desta forma, evitando trabalho desnecessário. É óbvio que este é um exemplo bem simples do uso de Generics para reusabilidade de código. Você pode usar muito mais recursos do que os mostrados neste artigo, pense na sua lógica de negócio e tente resolver o máximo possível apenas com uma classe Abstrata, assim você evita retrabalho e tem mais tempo para término do projeto.

Podemos, por exemplo, criar uma classe utilizando um recurso muito interessante do Generic que é a criação de uma instância apenas através do seu tipo, como é o caso do nosso método “getInstanceofBean()” que quase que “milagrosamente” cria uma nova instância do nosso objeto apenas conhecendo seu tipo que foi passado pelo “PessoaCrudImpl<Pessoa>”. Na Listagem 3 fazemos uso deste método aplicando uma lógica de negócio própria, apenas para você entender que poderíamos aplicar diversas funcionalidades para este método.

public class PessoaCrudImpl extends AbstractCrud<Pessoa> {
   
   
   public Pessoa prepararInsercaoDePessoa(){
         Pessoa pessoa = getInstanceOfbean();
         pessoa.setPessoaFisica(new PessoaFisica());
         pessoa.setContato(new Contato());
         
         pessoa.setNumerador(criarNumeracaoDePessoa());
         
         if (pessoa.getNumerador > 100){
                pessoa.setNivel(pessoa.getNumerador() / 5);
         }
         
         return pessoa;
   }
 
}
Listagem 3. Aumentando os recursos de PessoaCrudImpl

Veja na Listagem 4 uma alternativa a instanciação de objetos usando generics.

package org.foo.com;
 
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
 
/**
 * Basically the same answer as noah's.
 */
public class Home<E>
{
 
    @SuppressWarnings ("unchecked")
    public Class<E> getTypeParameterClass()
    {
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }
 
    private static class StringHome extends Home<String>
    {
    }
 
    private static class StringBuilderHome extends Home<StringBuilder>
    {
    }
 
    private static class StringBufferHome extends Home<StringBuffer>
    {
    }   
 
    /**
     * This prints "String", "StringBuilder" and "StringBuffer"
     */
    public static void main(String[] args) 
     throws InstantiationException, IllegalAccessException
    {
        Object object0 = new 
         StringHome().getTypeParameterClass().newInstance();
        Object object1 = new
         StringBuilderHome().getTypeParameterClass().newInstance();
        Object object2 = new 
         StringBufferHome().getTypeParameterClass().newInstance();
        System.out.println(object0.getClass().getSimpleName());
        System.out.println(object1.getClass().getSimpleName());
        System.out.println(object2.getClass().getSimpleName());
    }
 
}
Listagem 4. Alternativa para instanciar objetos com generic

No exemplo da Listagem 4 não retornamos o objeto instanciado mas sim uma classe para então podermos instanciar o objeto em outro local, no nosso caso, utilizamos o método “main” para tal tarefa. Criamos três objetos distintos apenas com o uso de Generics.

Com isso, podemos concluir que o uso do Generics sem dúvida é um assunto vasto e muito poderoso. O objetivo deste artigo foi mostrar o uso de tal recurso aplicado a reusabilidade de código, dando foco a instanciação de objetos utilizando Generics.