Mostrando postagens com marcador generics. Mostrar todas as postagens
Mostrando postagens com marcador generics. Mostrar todas as postagens

terça-feira, 7 de janeiro de 2014

GenericTableModel com Reflection e Annotation


GenericTableModel com Reflection e Annotations

Vou falar um pouco sobre TableModel, Reflection e Annotation.

O que é o TableModel? 
A maioria dos componentes swing possuem uma arquitetura que separa model e view, de forma que é definida uma interface para cada model. Dentro deste contexto, TableModel é a interface que representa o model da JTable. Se não informarmos qual será o model, a JTable usará por padrão uma instância de DefaultTableModel.

Ai que muitas discussões ocorrem. A quem diga para nunca usar DefaultTableModel, alguns já garantiram a sua morte, mas como poucos tirariam tempo para desenvolver o seu TableModel muitos o utilizam e não veem problema nisso. Mas uma coisa é certa, a complexidade de manter um DefaultTableModel é grande. 

Segundo VinnyGodoy, moderador do maior forum java do Brasil(www.guj.com.br), esses seriam os argumentos para não usar esse modelo.

Não use DefaultTableModel!!!!
Motivos:
1. É mais difícil que escrever seu próprio TableModel;
2. É mais lento (usa classes sincronizadas);
3. Ocupa mais espaço em memória (duplica seus dados);
4. Deixa o código mais confuso e difícil de manter;
5. Usa casts inseguros;
6. Força que você tenha que exibir informações desnecessárias (como o ID) na tabela, ou controlar o ID numa lista separada;
7. Faz a sua mulher te deixar, o leite da sua geladeira azedar, e pessoas apontarem o dedo para você na rua;


Por que a JTable precisa de um TableModel?
A JTable chama os métodos do TableModel para obter informações sobre o conteúdo da tabela, como número de linhas e colunas, conteúdo de cada célula, etc.

Qual o papel da classe AbstractTableModel? 
AbstractTableModel é uma classe abstrata que oferece a implementação de alguns métodos da interface TableModel, além de um conjunto de métodos úteis. Portanto o usual é estender esta classe ao invés de implementar TableModel diretamente.

Ok, entendi. Tenho que Criar meu próprio TableModel extendendo a classe AbstractTableModel e cumprindo o contrato sobrescrevendo seus métodos. Mas a onde entra Reflection nisso?

Bom, se você assim como eu, também não tinha ideia de o que era Reflection e como ela funciona, posso garantir, e mais fácil do que parece.

Por que Reflection? 
Java permite que, enquanto escrevemos nosso código, tenhamos total controle sobre exatamente o que vai ser executado, o que faz parte do nosso sistema.
Em tempo de desenvolvimento, olhando nosso código, sabemos quantos atributos uma classe tem, quais métodos ela tem, qual chamada de método está sendo feita e assim por diante.
O que a reflection faz é isso. Olhar para dentro de nossa classe e descobrir seu conteúdo.

O ponto de partida de reflection é a classe "Class".
Através dela conseguimos obter informações como os atributos dela, os métodos, os construtores etc.

Reflection é um pacote do Java que permite chamadas dinâmicas em tempo de execução sem precisar conhecer as classes e objetos envolvidos quando escrevemos nosso código (tempo de compilação).

É ideal para resolvermos determinadas coisas que nosso programa só descobre quando realmente estiver o rodando.

Neste caso, ao criar uma GenericTableModel, ela deve estar pronta a receber qualquer list("Genérico, não?") como parâmetro e mostrar a partir de uma jTable seus dados contidos.
O TableModel não sabe que atributos tem esse list, mas através de reflection, ela consegue "descobrir" esses campos e gerar a jTable.

É possível fazer muitas coisas usando reflection. Apenas para citar algumas possibilidades:
* Listar todos os atributos de uma classe e pegar seus valores em um objeto específico;
* Instanciar classes cujo nome só vamos conhecer em tempo de execução;
* Invocar métodos dinamicamente baseado no nome do método como String;
* Descobrir se determinados pedaços do código foram anotados com as anotações do Java 5.

Ex:
public class Pessoa {
 private String nome;
 private String email;
 private int idade;
 // Getters, setters, métodos de pessoa...
}

Class classe = Pessoa.class;
for (Field field : classe.getDeclaredFields()) {
System.out.println(field.getName());
}

A saída Será:
nome
email
idade

Dessa maneira, posso deixar o TableModel Genérico.
A cada execução, a cada classe distinta, ele descobre seus atributos e age sobre eles da mesma forma. 

Ok. Funcional. Mas e se eu quiser que meu GenericTabelModel não atue sobre todos os atributos, como eu faço para "marcar" um atributo ignorado? Como eu Faço para marcar apenas o Atributo que quero que apareça na jTable?

Bom, e ai que entra as Annotations. 
Não vou explicar o funcionamento de annotation, ja que o colega Bruno Lima, fez um post sobre o seu funcionamento. (@Annotations "metadados" que não interferem diretamente no código anotado) 

Posso criar anotações e usar na minha classe, onde em tempo de execução e de acordo com a annotation criada o programa age da maneira que eu especificar. 

No casso do GenericTableModel trabalhei com as Seguintes Annotations: 
@TableModel - Anotação de classe, atua sobre todos os seus atributos, coloca todos na jTable.
@ColumnIgnore - Anotação de campo. o Atributo que possuir essa anotação, não irá aparecer na jTable.
@Column - Anotação de Campo e Metodos. O atributo que possuir essa anotação vai aparecer na jTable.     * colName() = Pode-se alterar o nome da coluna, 
    * colPosition() = Posição de coluna na jTable 
    * formatt() = formatar um campo, sendo sua saida uma String. 


Funcionamento:
         GenericTableModel tableModel = new GenericTableModel(getData(), 
            Pessoa.class);
 JTable table = new JTable(tableModel);
 JFrame frame = new JFrame("GenericTableModel");
 table.setModel(tableModel);



private List getData() {
List list = new ArrayList();
list.add(new Pessoa("Marcos", "viny_scholl@hotmail.com", 21, "xxxxx", 1000.5));
list.add(new Pessoa("Marco", "tony_scholl@hotmail.com", 21, "bbbbbb", 21.75 ));
list.add(new Pessoa("Felipe", "felipe_scholl@hotmail.com", 27, "qwert", 75.40));
return list;
}

Exemplos de Execução da GenericTableModel com diferentes Annotations.
Ex1:
  
@TableModel(includeAll=true)
public class CopyOfPessoa {
 private String nome;
 private String email;
 @ColumnIgnore
 private int idade;
 private String senha;
 private double saldo;
}
Saída: 

 ----------------------------------------
Ex2:
 
@TableModel(includeAll=true)
public class CopyOfPessoa {
 @ColumnIgnore
 private String nome;
 @ColumnIgnore
 private String email;
 @ColumnIgnore
 private int idade;
 @ColumnIgnore
 private String senha;
 private double saldo;
}
Saída: 

----------------------------------------
Ex3:
 
@TableModel(includeAll=false)
public class CopyOfPessoa {

 private String nome;
 @Column
 private String email;
 private int idade;
 @Column
 private String senha;
 @Column
 private double saldo;
}
Saída: 

---------------------------------------- 
Ex4:
Com a Formatação possivel Pelo @Column
 
@TableModel(includeAll=false)
public class CopyOfPessoa {

 @Column(colName = "Nome", colPosition = 0)
 private String nome;
 @Column(colName = "Email", colPosition = 1)
 private String email;
 @Column(colName = "Idade", colPosition = 4)
 private int idade;
 @Column(colName = "Pass", colPosition = 3)
 private String senha;
 @Column(colName = "Saldo", colPosition = 2, formatter="R$ %,#.2f")
 private double saldo;
}
Saída: 



Assim, posso trabalhar de diferentes formas e com diferentes resultados de saída, a partir de uma unica classe usando Reflection e Annotations. 

Temos uma tabela Dinâmica. Uma GenerictableModel.

Referencias:
http://www.guj.com.br/java/199067-redimensionar-jtable---pra-variar--resolvido-/2#1001295
http://www.guj.com.br/java/149861-recuperar-id-de-jtable/2#813171
http://devsv.wordpress.com/2012/07/08/como-implementar-um-tablemodel/
http://markytechs.wordpress.com/2009/05/29/objecttablemodel/
http://www.guj.com.br/java/231928-morte-definitiva-ao-default-table-model#1399642
Caelum FJ-16 -Laboratório Java com Testes, XML e Design Patterns


Marcos Vinicius Scholl:
Graduando em Tecnologia em Análise e Desenvolvimento de Sistemas - TADS 
Universidade Federal do Rio Grande - FURG 
Instituto Federal de Educação, Ciência e Tecnologia do Rio Grande do Sul IFRS - Campus Rio Grande 
Rua Eng. Alfredo Huch, 475 - Rio Grande, RS - Brasil - 96201-460 

Sobre o autor:
Marcos Vinicius Scholl estuda na área de Tecnologia de Informação. É entusiasta na área de tecnologia. Gremista de paixão e anticolorado por prazer!

segunda-feira, 30 de setembro de 2013

Tipos Genéricos (Generic Types)

Vamos imaginar que precisamos criar uma classe chamada Celular, mas nós não sabemos qual tipo de celular que iremos ter por que afinal existem vários. Ele pode ser de um chip, dois, três ou quatro chips, ser touch ou não e por aí vai. Então vamos criar as classes.

public class CelularUmChip {

    private String marca;
    private int numeroChip1;
   
    public CelularUmChip(String marca, int numeroChip1, 
            boolean ehTouch) {
        this.marca = marca;
        this.numeroChip1 = numeroChip1;
        this.ehTouch = ehTouch;
      
    }    
}

class CelularDoisChips {

    private String marca;
    private int numeroChip1, numeroChip2;    

    public CelularDoisChips(String marca,int numeroChip1, 
            int numeroChip2) {
        this.marca = marca;
        this.numeroChip1 = numeroChip1;
        this.numeroChip2 = numeroChip2;
    }
}

Deu de ver que o código vai ficar repetido né? É aí que utilizamos os Generics. Porém os Generics só vieram há ser implementados na versão 1.5 do Java, até então era necessário pegar os tipos como Object e fazer casting para qual o tipo desejado.
A classe Celular ficaria assim:
public class Celular{
 private Object cellphone;

 public Celular(Object cellphone){
  this.cellphone = cellphone;
 }
 
 public Object getCellphone(){
  return cellphone;
 }
}

E no Main seria necessário fazer o casting como representado no código abaixo.

public class Main{
 public static void main(String[] args){
  Celular c = new Celular(new CelularUmChip("Motorola", 81909090));
  
  CelularUmChip c1 = (CelularUmChip)c.getCellphone();
 }
}

Programando desta maneira seria muito fácil ocorrer erros de Casting, como o clássico "ClassCastException", que dispara quando estamos instanciando a classe errada.

Mas a partir do Java 1.5 já se pode usar os Generics. O que ele faz é instanciar uma classe para que ela receba qualquer tipo de classe ou qualquer tipo primitivo, como int, double e etc. Para instanciar uma classe Generics basta colocar ao lado do nome um <T>, assim já estamos dizendo ao compilador que ele irá receber tal classe quando estiver em compilação. O que vai entre <> não se limita somente ao “T”, há outros placeholders:


E – Element

K – Key

N – Number

T – Type

V – Value

Os Generics permitem o uso de mais de um placeholder no mesmo <>, como por exemplo:
Example <T, N, V>
Vamos refazer nosso código agora utilizando Generics.

public class Celular<T>{

    private T cellphone;

    public Celular(T cellphone) {
        this.cellphone = cellphone;
    }    

    public T getCellphone() {
        return cellphone;
    }    
}
Agora nossa classe Celular vai poder receber qualquer uma das outras que foram declaradas antes.
public class Main {
    public static void main(String[] args) {
        
      Celular<CelularUmChip> c = new Celular(new CelularUmChip("Motorola", 
              81909090));
      
        Celular<CelularDoisChips> c2 = new Celular(new CelularDoisChips("Nokia",
                90909090, 99999090));
    }
}

Referência:

Java Efetivo do Joshua Block, Arquiteto de Software na Google
Site Tiexpert, post do Denys William Xavier
Site Plugmaster, post do Dayvid Lima.