Java - Generics
Por Thiago Almeida
(Thiago2010.2@gmail.com)
Programação genérica é um paradigma da computação que se refere a um tipo de abstração no processo de desenvolvimento de software, voltada para a construção de estruturas genéricas tão eficientes quanto se fossem elaboradas para um determinado tipo específico.



Na Coluna Java de dezembro, veremos uma introdução a programação genérica em Java. Generics em java está associado com a idéia de criar métodos e classes genéricas que podem ser declaradas uma vez, mas usadas com vários tipos de dados diferentes. Os algoritmos genéricos podem ser simples ou complexos, desde que atendam aos requisitos especificados. Em Java, o uso de generics pode ser percebido principalmente no framework Collections.

Como exemplo de programação genérica, suponha que você foi chamado para implementar um sistema de uma loja e ela é composta pelos seguintes artigos de venda: bicicleta(marca, tamanho do aro e quantidade de marchas), máquina de lavar(quantidade de peso para lavagem e marca) e TV(marca e polegadas). A princípio, o sistema deverá ter duas funcionalidades: adicionar um novo item que chegou ao estoque e retornar os itens que a loja teem em estoque. Uma solução para esse sistema seria:

package produtos;
public class Bicicleta {
        
        private String aro;
        private String marca;
        private int marchas;
        public Bicicleta(String aro, String marca, int marchas) {
                this.aro = aro;
                this.marchas = marchas;
                this.setMarchas(marchas);
        }
        public String getAro() {
                return aro;
        }
        public String getMarca() {
                return marca;
        }
        public int getMarchas() {
                return marchas;
        }
        public void setMarchas(int marchas) {
                this.marchas = marchas;
        }
        public String toString(){
                return "Bicicleta de marca: "+marca+", de"+marchas+" marchas e aro"+aro;
        }
}

A classe Bicicleta é composta pelos atributos aro, marca e marchas, e os métodos acessadores(gets) e modificadores (sets) correspondentes a cada atributo.

package produtos;
public class TV {
        private String marca;
        private String polegadas;
        public TV(String polegadas, String marca) {
                this.marca = marca;
                this.polegadas = polegadas;
        }
        public String getMarca() {
                return marca;
        }
        public String getPolegadas() {
                return polegadas;
        }
        public String toString(){
                return "TV da: "+marca+" e "+polegadas+"polegadas.";
                
        }
}

A classe TV, que possui os atributos: marca e polegadas, e somente métodos acessadores (gets) já que não podemos mudar a marca ou as polegadas de uma TV.

package produtos;
public class MaquinaDeLavar {
        private String marca;
        private String pesoDeLavagem;
        public MaquinaDeLavar(String marca,String pesoDeLavagem) {
                this.marca = marca;
                this.pesoDeLavagem = pesoDeLavagem;
        }
        public String getMarca() {
                return marca;
        }
        public String getPesoDeLavagem() {
                return pesoDeLavagem;
        }
        public String toString(){
                return"Maquina de lavar da marca: "+marca+" de "+pesoDeLavagem+"Kgs";
        }
}

A classe MaquinaDeLavar, é composta pelos atributos: marca e pesoDeLavagem, e somente métodos acessadores(gets) correspondentes, pelo mesmo motivo da classe TV, já que não podemos mudar suas características.

Classe Artigo:
package solucaoSemGenerics;
publicclass Artigo{
        Bicicleta bike;
        MaquinaDeLavar maq;
        TV tv;
        public Artigo(Bicicleta bike) {
                this.bike = bike;
        }
        public Artigo(MaquinaDeLavar maq){
                this.maq = maq;
        }
        public Artigo(TV tv){
                this.tv = tv;
        }
        public Object getArtigo(){
                if(tv ==null&&maq== null){
                        returnbike;
                }
                else if(bike ==null&&maq== null){
                        returntv;
                }else{
                        returnmaq;
                }
        }
        
}

A classe Artigo é composta por três tipos de produtos, três construtores e um método getArtigo() que necessita de if/else para diferenciar o retorno. E a classe Loja é composta por uma lista de artigos de vendas.

import java.util.List;
import solucaoSemGenerics.Artigo;
public class Loja {
        List artigos;
        
        public Loja() {
                artigos = new ArrayList();
        }
        public void addArtigo(Artigo art){
                artigos.add(art);
        }
        public List getArtigos(){
                return artigos;
        }
}

A classe Artigo possui três construtores, um para cada tipo específico de produto. Possui ainda, o método getArtigo() com if/else para diferenciar qual atributo deve ser retornado como Object. Possivelmente, , em uma aplicação que use objetos da classe Artigo, seria necessário um cast no retorno do método getArtigo(),para as classes Bicicleta, TV ou MaquinaDeLavar e, só assim, poderíamos acessar os métodos especificados nessas classes. Note que, os três construtores são bem parecidos, diferenciando, apenas do tipo do parâmetro de entrada. Essa solução funciona. Mas, não é a mais eficiente.

Outra forma, usando Generics, seria declarar um tipo genérico T na classe Artigo e, a partir dessa declaração, criar um único construtor do tipo T que representa qualquer objeto que poderia ser passado como parâmetro, e depois adicionar o objeto Artigo à classe Loja. Assim, poderiamos representar todos os objetos que podem ser vendidos na loja sem a necessidade de usar cast e/ou instanceof para descobrir qual objeto está sendo manipulado e sem precisar alterar a classe Loja.

Solução:
package solucaoComGenerics;
publicclass Artigo {
        T artigo;
        public Artigo(T artigo) {
                this.artigo = artigo;
        }
        public T getArtigo(){
                returnartigo;
        }
}

Nessa solução, abstraímos o tipo de objeto que vai ser passado para a classe Artigo. Note que a classe Artigo anteriormente era composta por três tipos de construtores específicos, e que, se, em algum momento, fossem adicionados novos produtos a loja, precisaríamos adicionar novos atributos e, consequentemente, novos construtores. Agora, um único tipo T representa qualquer objeto que pode ser adicionado á loja. Se for criada uma nova mercadoria, o programa funcionará sem alterações.

Veja que o novo tipo T está entre as tags<>, que, em Java, representam a declaração de um tipo genérico. Note que, com a nova declaração, conseguimos reduzir três construtores para apenas um. Eliminando ainda if/else de possíveis casts.

Esse modelo é bastante usado nas implementações de coleções, já que o tipo que vai ser armazenado é, quase sempre, desconhecido.

É importante ressaltar que as restrições declaradas nas classes genéricas devem ser respeitadas. Se, por exemplo, foi dito que esse tipo genérico extends de Comparable, o método compareTo dessa interface deve ser implementado, pois, métodos como o sort utilizam essa declaração em sua ordenação.

Criar estruturas genéricas é uma importante ajuda na simplificação de nossas aplicações e na modelagem de soluções genéricas que podem ser aplicadas não só em uma aplicação específica, criando um grande desacoplamento nos nossos códigos.

Os códigos, simulando o antes e o depois da aplicação, podem ser encontrado aqui.


FONTES: http://www.tiexpert.net/programacao/java/generics.php http://docs.oracle.com/javase/tutorial/java/generics/ http://www.deitel.com/articles/java_tutorials/20050909/ImplementingGenericMethods.htm
Jornal PETNews - Edição: Jéssika Renally - Revisão: Tiaraju Smaneoto e Lívia Maria
Grupo PET Computação UFCG, 2012. All rights reserved.