GENERICS
Generic é um recurso que foi implementado no Java 1.5 que permite escrever códigos mais seguros e fáceis de ler pelo fato de utilizar menos cast (conversão de um objeto em outro). O uso do generic faz com que a programação seja mais genérica havendo, portanto um melhor reuso do código para objetos de muitos tipos diferentes. Esse reuso pode ser feito através de classes e métodos genéricos. Outro uso importante do generic é em coleções, onde tem a função de especificar somente um tipo de objeto que uma coleção específica terá, tornando mais segura a implementação.
O uso de generics para criação de métodos faz com que possamos criar métodos que aceitam vários tipos de objetos. Imagina por exemplo uma situação onde seria preciso imprimir um array de vários tipos, precisando fazer um método para cada tipo de objeto (Integer, Double e String) para que seja resolvido o problema. Com o uso do generics seria preciso somente fazer um método genérico que aceitaria um array de tipo genérico definido como tipo E (através dos sinais de < >) e que percorreria esse array para imprimí-lo, como no exemplo abaixo. Vale lembrar que generics trabalha somente com tipo de referência sendo impossível utilizar tipos primitivos, sendo errado passar para o método imprimeArray(E[ ] array) um array do tipo primitivo int (exemplo int[ ]).
Exemplo do uso de Generics:
O programa a seguir mostra o uso do método genérico.
/** * Síntese * Objetivo: Mostrar o uso do método genérico * Entrada: Nenhuma (dados atribuídos pelo programa) * Saída: Impressão de vários tipos de array */ public class MetodoGenerico { public static void main(String[] args) { Integer[] arrayInt = {1,1,2,3,5}; Double[] arrayDouble = {1.1,6.2,7.3,6.6,9.9}; String[] arrayString = {"Do","Re","Mi","Fa","So"}; System.out.print("Array de Inteiro : "); imprimeArray(arrayInt); System.out.print("Array de Double : "); imprimeArray(arrayDouble); System.out.print("Array de String : "); imprimeArray(arrayString); } public static < E > void imprimeArray(E[] array){ for (E elemento : array) System.out.print(elemento+" "); System.out.println(); } }
A execução do programa anterior apresentaria a saída de dados na console como representado na figura a seguir:
A classe genérica é outro exemplo da implementação do generics onde podemos definir em uma classe elementos genéricos que somente serão definidos quando for criado um objeto da classe, portanto esses elementos que forem do tipo definido com generic não terão uma referência a um tipo específico. No exemplo abaixo podemos ver a definição de uma classe genérica e o seu uso.
/** * Síntese * Classe com atributo definido como genérico. */ public class ClasseGenerica < T >{ private T atributo; public T getAtributo() { return atributo; } public void setAtributo(T atributo) { this.atributo = atributo; } }
O programa a seguir mostrar a utilização de uma classe genérica
/** * Síntese * Objetivo: Mostrar a utilização de uma classe genérica * Entrada: Nenhuma * Saída: Visualização de cada objeto diferente criado através da * ClasseGenerica */ public class ExemploClasseGenerica { public static void main(String[] args) { ClasseGenerica< String > objStr = new ClasseGenerica< String >(); ClasseGenerica< Integer > objInt = new ClasseGenerica< Integer >(); ClasseGenerica< Boolean > objBoolean = new ClasseGenerica< Boolean >(); objStr.setAtributo("String objeto generico"); objInt.setAtributo(100); objBoolean.setAtributo(true); //Objetos Criados System.out.println("Criado como String : "+objStr.getAtributo()); System.out.println("Criado como Integer : "+objInt.getAtributo()); System.out.println("Criado como Boolean : "+objBoolean.getAtributo()); } }
O Resultado das Duas Classes acima resultam na saída da console abaixo:
Podemos aplicar com generics uma limitação dos tipos de objetos. Observando os exemplos acima podemos perceber que tanto um método genérico como uma classe genérica podem receber um objeto de qualquer classe, portanto caso queiramos, podemos restringir a somente um tipo e subtipos de uma classe.
Um exemplo prático que podemos ter é relacionado à uma calculadora que somente recebe números, calcula e mostra o resultado, mas o que ocorreria se fosse possível uma calculadora receber um texto, o que ela iria fazer?
Como no exemplo abaixo no qual tem o método descobreGratificacao que retorna uma gratificação de acordo com o tempoEmpresa de um funcionário recebendo como parâmetro um objeto genérico pelo fato de poder ter outras classes que podem representar um empregado também.
/** * Síntese * Objetivo: Fazer uso de um método genérico sem limitar tipos * Entrada: Nenhuma * Saída: Nome do funcionário e o valor da gratificação */ public class ExemploLimitacao1 { public static void main(String[] args) { Empregado empregado1 = new Empregado("Joao",500.0,5); System.out.println("Gratificação do funcionario "+empregado1.getNome()+ " :"+descobreGratificacao(empregado1)); Empregado empregado2 = new Empregado("Maria",500.0,11); System.out.println("Gratificação do funcionario "+empregado2.getNome()+ " :"+descobreGratificacao(empregado2)); String empregado3 = new String("Emanoel"); System.out.println("Gratificação do funcionario "+empregado3+ " :"+descobreGratificacao(empregado2)); } public static < T > Double descobreGratificacao(T empregado){ Empregado auxEmpregado = (Empregado)empregado; Double aumento = 0.0; if(auxEmpregado.getTempoEmpresa()< 10) aumento = auxEmpregado.getSalario()*1.1; else aumento = auxEmpregado.getSalario()*1.3; return aumento; } }
Abaixo segue a Classe Encapsulada de Empregado:
public class Empregado { private String nome; private Double salario; private Integer tempoEmpresa; public Empregado(String nome, Double salario, Integer tempoEmpresa){ this.nome = nome; this.salario = salario; this.tempoEmpresa = tempoEmpresa; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public Double getSalario() { return salario; } public void setSalario(Double salario) { this.salario = salario; } public Integer getTempoEmpresa() { return tempoEmpresa; } public void setTempoEmpresa(Integer tempoEmpresa) { this.tempoEmpresa = tempoEmpresa; } }
A execução do programa anterior é representada na figura a seguir:
Pode-se observar que ocorre uma exceção pelo fato de o método não receber um objeto que é do tipo Empregado. Por isso é preciso especificar que esse método somente receberá objetos do tipo Empregado ou de qualquer subtipo (como ocorreria depois de ser adicionado ao programa uma classe Secretária que estenderia Empregado). O exemplo abaixo faz o uso dessa limitação de tipos através de generics.
/** * Síntese * Objetivo: Fazer uso de um método genérico com limitar tipos * Entrada: Nenhuma * Saída: Nome do funcionário e o valor da gratificação */ public class ExemploLimitacao2 { public static void main(String[] args) { Empregado empregado1 = new Empregado("Joao",500.0,5); System.out.println("Gratificação do funcionario "+empregado1.getNome()+" :"+descobreGratificacao(empregado1)); Empregado empregado2 = new Empregado("Maria",500.0,11); System.out.println("Gratificação do funcionario "+empregado2.getNome()+" :"+descobreGratificacao(empregado2)); //String empregado3 = new String("Emanoel"); //Não compilará pelo fato de não ser Empregado ou subclasse } public static < T extends Empregado > Double descobreGratificacao(T empregado){ Empregado auxEmpregado = (Empregado)empregado; Double aumento = 0.0; if(auxEmpregado.getTempoEmpresa()< 10) aumento = auxEmpregado.getSalario()*1.1; else aumento = auxEmpregado.getSalario()*1.3; return aumento; } }
Outro essencial uso de generics acontece no uso de coleção, onde podemos garantir que somente um tipo de objeto e suas subclasses serão aceitos em uma coleção o que garante uma maior segurança do programa. Observe por exemplo o ArrayList de nome inteiros do código abaixo onde há a intenção de armazenar somente objetos do tipo Integer, mas pode-se verificar que isso não é verdade. Na lista de inteiros pode ser adicionado objeto de qualquer tipo, como podemos ver a inclusão de um objeto do tipo String, fazendo com que a mesma não seja eficiente. Pode-se comparar o exemplo abaixo como se fosse uma lista de convidados para uma festa, na qual somente era para aceitar nomes de pessoas mas por algum motivo foi adicionado um número.
/** * Síntese * Objetivo: Mostrar a utilização de ArrayList sem o uso de generics * Entrada: Nenhuma * Saída: Exceção gerada pela incorreta utilização do ArrayList */ import java.util.ArrayList; public class ExemploGenerics1 { public static void main(String[] args) { ArrayList inteiros = new ArrayList(); inteiros.add(new Integer(2)); inteiros.add(new Integer(4)); inteiros.add("teste"); Integer total = 0; for (int i = 0; i < inteiros.size(); i++) total+=(Integer)inteiros.get(i); System.out.println("O total é : "+total); } }
A representação da execução do programa anterior na console seria como está na figura a seguir:
Pode-se observar que qualquer tipo de objeto pode ser inserido no ArrayList inteiros, porém quando formos fazer a soma ocorrerá uma exceção pelo fato de tentar somar um objeto que não é do tipo Integer. No exemplo da lista de convidados a pessoa que posteriormente for fazer uso dessa lista não saberá o que fazer com o número encontrado na lista.
Uma solução ideal seria usar o generics para "informar" à lista de inteiros que ela somente aceitará inteiros através dos sinais de maior e menor (< Integer >) na frente do tipo da coleção, sendo assim, caso for adicionado outro objeto que não for do tipo Integer, o código não será compilado evitando erro em tempo de compilação. Pensando na lista de convidados, toda vez que alguém fosse inserir um convidado na lista teria de ter certeza de que era um nome de pessoa. A implementação do mesmo código exemplificado acima usando generic ficaria assim:
/** * Síntese * Objetivo: Mostrar a utilização de ArrayList com o uso de generics * Entrada: Nenhuma * Saída: O resultado total do array de inteiros */ import java.util.ArrayList; public class ExemploGenerics2 { public static void main(String[] args) { ArrayList< Integer > inteiros = new ArrayList< Integer >(); inteiros.add(new Integer(2)); inteiros.add(new Integer(4)); //inteiros.add("teste"); // A linha de cima está comentada pelo fato de não poder //ser adicionada como código, // o array inteiros somente aceita objetos do tipo //inteiro(Pelo fato de utilizar GENERICS!). Integer total = 0; for (int i = 0; i < inteiros.size(); i++) total+=(Integer)inteiros.get(i); System.out.println("O total é : "+total); } }
A execução do programa anterior resultaria na demonstração representada na figura a seguir:
Como está sendo definido que a lista de inteiros somente terá objetos do tipo Integer, não será preciso fazer o cast do objeto da lista pelo fato de o Java ter certeza de que os objetos da lista são objetos do tipo Integer. Sendo assim o código seguinte mostra como resgatar o 1ยบ objeto da lista com e sem o uso do generic:
Integer inteiro = (Integer) inteiros.get(0); //Sem o uso do generic Integer inteiro = inteiros.get(0); // Com uso do generic
Atividade de Fixação
No intuito de fixar estes novos conteúdos abordados até o momento e verificar sua compreensão sobre os mesmos, são sugeridos alguns exercícios de fixação para serem resolvidos, por meio do seu clique no link de exercicío ao lado que permitirá o início da lista de exercícios. Bom trabalho!!