Annotations
Por Arthur Silva Freire
(arthur.freire@ccc.ufcg.edu.br)
Neste mês, a Coluna Java abordará um recurso que foi introduzido como padrão de linguagem na versão 1.5. Estamos falando de Annotations. Saiba o que é, pra que serve e como criar sua própria Annotation.

Você sabe o que são Annotations? Adivinha... são anotações. É um artifício que você tem para fazer alguma marcação em atributo, método, classe, entre outros. Dessa forma, podemos evitar, em muitos casos, arquivos XML de configuração, que tornam difícil a compreensão de alguns sistemas. Elas devem ser sempre declaradas antes do objeto que você quer anotar e uma @ (arroba) deve preceder o nome da sua anotação. Veja um exemplo simples abaixo .

@MinhaAnotacao
class MinhaClasse {
}
Nesta matéria, mostraremos dois exemplos para o uso de Annotations. Vamos ao primeiro. Todos nós sabemos que uma fase importante no desenvolvimento de software é a de testes. Você pode definir que todos os métodos que tiverem a anotação “@Testar” serão testados. Vamos agora criar essa anotação.

Testar.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Testar { }
//Observem que a nossa anotação deve ser anotada com meta-anotações.
//A anotação @Retention(RetentionPolicy.RUNTIME) diz que a VM deve manter 
//essas anotações em tempo de execução para que sejam lidas a partir da reflexão. 
//Outras opções seriam: SOURCE e CLASS.
//E a anotação @Target(ElementType.METHOD) indica que a nossa anotação só é 
//aplicada a métodos. Outras opções seriam: FIELD, TYPE (classe e interface), 
//PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, e PACKAGE.
Pronto, criamos a nossa anotação. Agora, criaremos uma classe que será testada.

ClasseParaTestar.java

public class ClasseParaTestar {
        //Como o propósito é só demonstrar as anotações, os métodos não fazem nada.
 
    @Testar
    public void metodo1() {
        throw new RuntimeException("Deu zebra!");
    }
 
    @Testar
    public void metodo2() {
    }
 
    @Testar
    public void metodo3() {
    }
 
    public void metodo4() {
    }
 
    @Testar
    public void metodo5() {
        throw new RuntimeException("Ixi maria... Erro!");
    }
 
    public void metodo6() {
    }
 
    @Testar
    public void metodo7() {
    }
 
    public void metodo8() {
    }
}
Agora que nossos métodos já estão anotados, vamos criar a classe principal do nosso framework de testes.

Testador.java

public class Testador {
 
    public static void testar(Object obj) {
        Class classe = obj.getClass();
        int passou = 0;
        int falhou = 0;
 
        System.out.println("-------------Inicio dos Testes--------------");
        for (Method m : classe.getDeclaredMethods()) {
            if (m.isAnnotationPresent(Testar.class)) {
                try {
                    System.out.print("Testando o método: "; + m.getName());
                    m.invoke(obj);
                    passou++;
                    System.out.println(" => passou!");
                } catch (Throwable t) {
                    falhou++;
                    System.out.println(" => falhou! " + t.getCause());
                }
            }
        }
        System.out.println("---------------Fim dos Testes---------------");
        System.out.println("Passaram: " + passou + ", Falharam: " + falhou);
    }
}
Para rodar, basta chamar o método estático passando um objeto da classe que você deseja testar.
Testador.testar(new ClasseParaTestar());
Esta será a saída para o primeiro exemplo:
-------------Inicio dos Testes--------------
Testando o método: metodo1 =>; falhou! java.lang.RuntimeException: Deu zebra!
Testando o método: metodo2 =>; passou!
Testando o método: metodo3 =>; passou!
Testando o método: metodo5 =>; falhou! java.lang.RuntimeException: Ixi maria... Erro!
Testando o método: metodo7 =>; passou!
---------------Fim dos Testes---------------
Passaram: 3, Falharam: 2
O nosso segundo exemplo consiste em criar uma anotação para validar os atributos. Vamos criar uma anotação para verificar se um atributo contém um determinado texto e outra para verificar se o atributo é positivo.

ValidarPositivo.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ValidarPositivo { }

ValidarContemTexto.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ValidarContemTexto {
    int min() default 1;
}
//As anotações podem conter atributos para realizar alguma lógica durante o processamento dessas.
//No nosso caso, a anotação @ValidarContemTexto irá validar se o atributo que está 
//decorado com a anotação possui pelo menos min caracteres (que por default é 1).
//Reparem que a meta-anotação @Target é ElementType.FIELD, porque vamos utilizar essas anotações 
//para decorar atributos.
A nossa classe de testes será a seguinte:

Usuario.java

public class Usuario {
 
    @ValidarContemTexto(min = 10)
    private String login;
 
    @ValidarContemTexto(min = 20)
    private String senha;
 
    @ValidarPositivo
    private int nivel;
 
    //Construtores, getters e setters
    //...
}
Agora, a classe responsável pela validação:

Validador.java

public class Validador {
 
    public static boolean validar(Object obj) {
        Class classe = obj.getClass();
        boolean ok = true;
 
        for (Field f : classe.getDeclaredFields()) {
            f.setAccessible(true);
 
            //Annotation @ValidarPositivo
            if (f.isAnnotationPresent(ValidarPositivo.class)) {
                try {
                    int num = Integer.parseInt(f.get(obj).toString());
                    if (num <= 0) {
                        ok = false;
                        throw new Exception();
                    }
                } catch (Throwable t) {
                    System.out.println(f.getName() + " deve ser um valor inteiro positivo.");
                }
            }
 
            //Annotation @ValidarContemTexto
            if (f.isAnnotationPresent(ValidarContemTexto.class)) {
                ValidarContemTexto anotacao = f.getAnnotation(ValidarContemTexto.class);
                try {
                    String texto = f.get(obj).toString();
                    if (texto.length() < anotacao.min()) {
                        ok = false;
                        System.out.println(f.getName() + " deve possuir pelo menos " + anotacao.min() + " caracteres.");
                    }
                } catch (Throwable t) {
                    System.out.println(f.getName() + ": " + t.getCause());
                }
            }
        }
        return ok;
    }
}
O código a seguir cria três usuários e executa a validação com cada um deles.
ArrayList usuarios = new ArrayList();
usuarios.add(new Usuario("login", "senha", 2));
usuarios.add(new Usuario("arthur", "arthur", -5));
usuarios.add(new Usuario("jucamlkdoido", "senhajucasenhajucasenhajuca", 1));
 
for (Usuario u : usuarios) {
    System.out.println("Validando usuario: " + u.getLogin());
    if (Validador.validar(u)) {
        System.out.println("Usuário OK.");
    } else {
        System.out.println("Corrija os erros.");
    }
    System.out.println();
}
O resultado é este:
Validando usuario: login
login deve possuir pelo menos 10 caracteres.
senha deve possuir pelo menos 20 caracteres.
Corrija os erros.
Validando usuario: arthur
senha deve possuir pelo menos 20 caracteres.
nivel deve ser um valor inteiro positivo.
Corrija os erros.
Validando usuario: juca
Usuário OK.
Isso é o que tínhamos pra mostrar nesta matéria, uma breve introdução a Annotations. Caso queira se aprofundar mais, acesse o link da documentação.
Jornal PETNews - Edição: Jeymisson Oliveira - Revisão: Iago Araújo e Joseana Fechine
Grupo PET Computação UFCG, 2012. All rights reserved.