Trabalhando com annotations em Java

nov 05 2009

Um código com annotations (sem excesso) é um código bonito de se ver. Muitas vezes, algumas abordagens para definir classes e regras do sistema podem ser facilmente substituídas pelas annotations. Imagine um framework que manipula alguns objetos para fazer alguma coisa: você fornece junto a este framework uma classe abstrata com um método abstrato que deve ser implementado pelos objetos que serão manipulados pelo seu framework ou pode explorar outra abordagem, mas antes vamos analisar a ideia da classe abstrata…
Caso você forneça a classe abstrata, que deve ser estendida e blá blá blá, há alguns problemas: a possível necessidade de herdar de uma classe que modela objetos não gerenciados pelo framework; o forte acoplamento das classes de regra de negócio ao framework; o código na classe vai ficar bem mais feio; etc.

O que fazer então? Há uma alternativa: usar annotations. Anotando seu código, ele não vai ficar feio, vai parecer mais “lógico”, e a coisa vai ser bem mais bonita (repetição é a lei). Você só precisa de um pouco de reflection e voilà!

Tudo isso é muito legal, mas sem exemplo não vale nada. Vamos a um hands on legal (ou não). Explicando rapidamente o caso, teremos uma anotação que utilizaremos em atributos da nossa classe. Esta anotação terá um nome bem simbólico: AtributoAnotado. A definição de uma anotação se parece com a definição de uma interface:

@Retention(RetentionPolicy.RUNTIME)
public @interface AtributoAnotado {
    String classificacao();
}

Definimos nossa anotação com um public @interface …, chamamos ela então de AtributoAnotado. Mas há mais código aí, definimos dentro da anotação um atributo para ela: trata-se, na verdade, de um método. Será possível entender melhor este método quando a classe for anotada.

Além disso, temos uma anotação antes da definição: sim, nós anotamos nossa anotação! Mas o que significa RetentionPolicy? Trata-se da política de retenção (óóóó), indica como e onde as anotações serão enxergadas, existem três possibilidades: CLASS, RUNTIME e SOURCE.

A RetentionPolicy CLASS indica que a anotação será gravada (compilada) junto ao arquivo da classe (.class), mas será ignorada em tempo de execução: você não consegue manipulá-la com seu programa rodando, nem obter informações sobre itens anotados. Bom, não é essa a policy que precisamos para o nosso exemplo. Vamos à segunda alternativa, a RetentionPolicy RUNTIME: trata-se da policy que permite que as informações da anotação sejam acessadas em tempo de execução (de forma reflexiva). Este é o tipo que precisamos! Apenas para conhecimento, o tipo SOURCE é ignorado pelo compilador da JVM. É uma policy que indica que a anotação deve ser ignorada, é útil para documentação de código utilizando anotações.

Uma vez que definimos a nossa anotação, vamos criar uma classe com três atributos e anotar apenas dois deles. Depois vamos imprimir o nome e o valor dos atributos anotados somente, tudo na base da reflexão. Vamos então à classe Pessoa:

public class Pessoa {

    @AtributoAnotado(classificacao = "1")
    private String nome;

    private Integer idade;

    @AtributoAnotado(classificacao = "2")
    private String sexo;

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public Integer getIdade() {
        return idade;
    }

    public void setIdade(Integer idade) {
        this.idade = idade;
    }

    public String getSexo() {
        return sexo;
    }

    public void setSexo(String sexo) {
        this.sexo = sexo;
    }

}

Veja que o método classificacao aqui virou um “atributo” da anotação. Por fim, vamos ao método imprimirAtributosAnotados, que recebe um objeto do tipo pessoa e imprimir o nome e o valor de todos os atributos anotados:

public void imprimirAtributosAnotados(Pessoa obj) throws IllegalArgumentException, IllegalAccessException {
    Class cls = obj.getClass();
    Field[] camposDaClasse = cls.getDeclaredFields();
    for (Field campo : camposDaClasse) {
        if (campo.isAnnotationPresent(AtributoAnotado.class)) {
            campo.setAccessible(true);
            System.out.println(campo.getName() + ": " + campo.get(obj));
        }
    }
}

Uma rápida explicação: na linha 2 descobrimos a classe do nosso objeto e na linha 3 pegamos um vetor com todos os atributos da classe. Daí pra frente é só navegar em todos os atributos usando um for e sair perguntando se o atributo está anotado (através do método isAnnotationPresent da classe Field). Caso o atributo esteja anotado, imprimimos seu nome e seu valor =)

Uma dica sobre reflection

Códigos utilizando reflection diretamente são escrotos meio confusos e podem gastar muito do seu tempo. Pensando nisso, uns insanos caras legais da Caelum criaram uma API alternativa para trabalhar com reflection, chamada Mirror. Trabalhando com Mirror, reflection parece até brincadeirinha de criança.

5 responses so far

  1. Legal o post Francisco.

    Hoje uso a annotation, por exemplo, para guardar um enum de perfis e anotar métodos indicando as respectivas permissões, criando assim um gerenciador de acesso.

    Mais pra frente irei criar um post sobre isso.

    Abraço. (:

  2. Opa Botelhos o/

    Sei como, é tipo como funciona no Django :)

    Abraços o/

  3. [...] único deve se chamar value. Para aprender um pouco mais sobre annotation você pode ler o artigo “Trabalhando com Annotations em Java” do nosso amigo Francisco Souza. [...]

  4. [...] Para aprender um pouco mais sobre annotation você pode ler o artigo “Trabalhando com Annotations em Java” do nosso amigo Francisco Souza. [...]

  5. [...] o post sobre Annotations em Java indicando o Mirror como ferramenta para facilitar o uso de reflection em Java. Trata-se de uma [...]

Leave a Reply

Spam protection by WP Captcha-Free