Numa discussão com um amigo, surgiu o tema sobre como classificar uma annotation. Será um interface ou uma classe, visto que embora seja definido com @ interface, podemos atribuir e retirar valores da annotation, indiciando que existe uma implementação dos métodos.
Na realidade, uma annotation é um tipo de interface. Tal como todas as classes que criamos extendem implícitamente a classe Object, sempre que criamos uma annotation esta extende implícitamente o interface Annotation. O interface annotation tem a seguinte definição:
public interface Annotation { boolean equals(Object obj); int hashpre(); String toString(); Class extends Annotation> annotationType(); }
Se quisermos criar uma nova annotation, podemos declará-la da seguinte forma:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @ interface Teste{ String umMembro(); }
Definimos uma annotation de teste com um membro do tipo String. Definimos com a Retention que a annotation vai estar disponível em runtime, e definimos no target que esta annotation pode ser utilizada em métodos. Utilizaríamos a annotation, por exemplo, neste simples bloco de código:
public class AnnotationsTest { @Teste(umMembro="textoMembro") public void umMetodo() {} }
Estamos então a anotar o nosso método com a anotação que criámos anteriormente, e a definir uma propriedade exemplo. A primeira questão a reparar é que a annotation é um interface mas estamos a atribuir-lhe valores quando utilizamos a annotation, e podemos consultar as annotations definidas em run-time (porque colocámos retention = RUNTIME na definição da annotation). Podemos simplesmente, adicionar um método main a esta nossa classe AnnotationsTest para tentar perceber um pouco melhor o que acontece.
public static void main(String[] args) throws NoSuchMethodException {< AnnotationsTest at = new AnnotationsTest(); Annotation[] annots = at.getClass().getMethod("umMetodo").getAnnotations(); for(Annotation annot : annots) { System.out.println("Annotation? " + annot.annotationType().isAnnotation()); System.out.println("Interface? " + annot.annotationType().isInterface()); System.out.println("Nome da classe da annotation: " + annot.getClass()); System.out.println("Nome do tipo da annotation: " + annot.annotationType().toString()); Teste t = (Teste) annot; System.out.println("Valor do umMembro: " + t.umMembro()); } }
Na linha 3, vamos buscar todas as annotations do método umMetodo(). Isto é possível, porque com a introdução das annotations no Java 5, todos os elementos sobre os quais podemos aplicar anotações passaram a implementar o interface AnnotatedElement, que fornece um modo de se aceder às annotations numa classe, método, variável, parametro ou mesmo package. Depois percorremos a anotações e fazemos uns testes para ver o que
nos diz sobre o que é uma anotação. O output é o seguinte:
Annotation? true Interface? true Nome da classe da annotation: class $Proxy3 Nome do tipo da annotation: interface zonaj.testes.annotations.Teste Valor do umMembro: textoMembro
As primeiras duas linhas confirma-se o que já se sabia, a Annotation é um interface. Na terceira linha vem a explicação para as nossas dúvidas: a classe da anotação é um $Proxy3, nitidamente um proxy gerado pelo compilador em run-time. Este proxy é que implementa automáticamente os métodos para as propriedades definidadas na nossa anotação, bem como
os métodos do interface Annotation.
Nas duas outras linhas de output, vemos que o annotationType é o Teste que criámos e vemos como obter o valor indicado na anotação do método. Este método de geração de proxies não é muito explicado mas funciona aproximadamente como o Annotation Processing Tool (apt) que é uma aplicação que nos permite criar factories para gerar proxies e processar anotações.
De resto, uma anotação não é uma classe, é um interface. Só é criado um proxy automaticamente para poder ser utilizada.
Parabêns! Gostei das explicações.
Mais de um modo conclusivo quais as vantagens de utilizar annotations?