Java Generics: Reutilizando seu código

Veja neste artigo como utilizar o poder do Generics em Java para reusabilidade de código, tornando seu projeto mais produtivo e de fácil manutenção.

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.

Veja Também:

Artigos relacionados