segunda-feira, 2 de março de 2015

Criar anotações, é possível ?

Sejam bem-vindos ao Preciso Estudar Sempre !!

Diversas vezes no desenvolvimento de um software, é comum o uso de anotações ou annotations. Mas, você sabe o que é uma annotation ? Você já criou uma alguma vez? Então, no post dessa semana falaremos sobre a criação de annotations e iremos entender como elas funcionam.

Já adianto logo, esse post é de nível intermediário. Então, eu espero que você já possua alguns conhecimentos sobre Java. Como foi citado no Preciso Estudar Sempre:@Embeddable e @Embedded no JPA, uma annotation é uma forma dinâmica de definição de metadata.

Para download do projeto pronto, clique aqui.

Antigamente os metadados eram definidos via XML mas, isso era muito trabalhoso e a oportunidade de cometer um erro era grande. Como sabemos, metadata é nada mais nada menos que, um dado sobre um dado. Mas, o que é um dado sobre um dado? Segundo a wikipedia, a definição de metadado é:

Metadados, ou Metainformação, são dados sobre outros dados. Um item de um metadado pode dizer do que se trata aquele dado, geralmente uma informação inteligível por um computador. Os metadados facilitam o entendimento dos relacionamentos e a utilidade das informações dos dados.
Como isso pode ser refletido no ambiente de desenvolvimento ? Vamos supor que exista uma classe chamada Morador.java e uma classe Carro.java. Elas fazem parte de um sistema de condomínio. Um morador possui um carro. Na classe de morador iremos definir metadados, como:
  • Data inicial de morada (quando ele começou a morar nesse condomínio)
  • Se é inquilino ou não
Na classe de carro iremos definir:
  • Numeração da documentação do carro
  • Placa do carro
IMPORTANTE: Isto é exemplo. Para esta situação, talvez a melhor abordagem(Morador e Carro) não seja annotation.

Agora que já sabemos quais metadados pertencem ao morador e ao seu carro, podemos criar nossas annotations.

CarroAnnotation.java
 package pkg;  
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;

 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
 public @interface CarroAnnotation {
      String numeracaoDocumentoCarro();
      String placa();
 }

MoradorAnnotation.java
 package pkg;  
 import java.lang.annotation.ElementType; 
 import java.lang.annotation.Retention; 
 import java.lang.annotation.RetentionPolicy; 
 import java.lang.annotation.Target; 

 @Target(ElementType.TYPE) 
 @Retention(RetentionPolicy.RUNTIME) 
 public @interface MoradorAnnotation { 
      String dataInicialMoradia(); 
      boolean isInquilino() default false; 
 } 

Neste momento, você deve estar se perguntando: "Ainnnn João, o que é esse @Target e esse @Retention ??????"

Para você definir uma anotação, você precisa definir como o compilador e JVM interpretarão ela e aonde ela será aplicada. Essas anotações servem para isso.

@Target = Aonde sua annotation será aplicada.
ElementType.TYPE -> será uma anotação de classe, interface ou enum;
ElementType.FIELD -> será uma anotação de atributo;
ElementType.METHOD -> será uma anotação de método;
ElementType.CONSTRUCTOR -> será uma anotação de construtor;
ElementType.ANNOTATION_TYPE -> será uma anotação de anotação;
ElementType.LOCAL_VARIABLE -> será uma anotação de variável local;
ElementType.PACKAGE -> será uma anotação de pacote;
ElementType.PARAMETER -> será uma anotação de parâmetro.

@Retention = Como a annotation será interpretada
RetentionPolicy.SOURCE -> será utilizada apenas no código fonte: é descartada pelo compilador e pela VM;
RetentionPolicy.CLASS -> será gravada na classe, mas não será utilizada pela VM;
RetentionPolicy.RUNTIME -> será gravada na classe e utilizada pela VM, assim, pode ser lida por reflexão.

Agora que já temos nossas annotations prontas, podemos aplicá-las às nossas classes.

Carro.java
 package pkg; 

 public class Carro {
      private String modelo;
      private String marca;
      private String ano;
      private String placa;

      public String getModelo() {
           return modelo;
      }

      public void setModelo(String modelo) {
           this.modelo = modelo;
      }

      public String getMarca() {
           return marca;
      }

      public void setMarca(String marca) {
           this.marca = marca;
      }

      public String getAno() {
           return ano;
      }

      public void setAno(String ano) {
           this.ano = ano;
      }

      public String getPlaca() {
           return placa;
      }

      public void setPlaca(String placa) {
           this.placa = placa;
      }
 }

Morador.java
 package pkg;  

 @MoradorAnnotation(isInquilino=true, dataInicialMoradia="01/02/1980") 
 public class Morador { 
      private String nome; 
      private String apartamento;
 
      @CarroAnnotation(numeracaoDocumentoCarro="04958JDKDM9485", placa="KDI-9930") 
      private Carro carro; 

      public String getNome() { 
           return nome; 
      } 

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

      public String getApartamento() { 
           return apartamento; 
      } 

      public void setApartamento(String apartamento) { 
           this.apartamento = apartamento; 
      } 

      public Carro getCarro() { 
           return carro; 
      } 

      public void setCarro(Carro carro) { 
           this.carro = carro; 
      } 
 } 

Pronto, nosso projeto está pronto !! Já criamos as annotations, aplicamos nas nossas classes mas falta uma coisa. Como iremos recuperar os valores que aplicamos nas anotações ??

Podemos recuperar os valores através de reflexão. Porém, isto só é possível pois configuramos nossas anotações como RetentionPolicy.RUNTIME. Lembre-se disso pois é importante.

Main.java
 package pkg;  

 import java.lang.annotation.Annotation; 
 import java.lang.reflect.Field; 

 public class Main { 
      public static void main(String[] args) { 
           new Main().exibeValoresAnnotation(); 
      } 

      public void exibeValoresAnnotation(){ 
           Class clazz = Morador.class; 

           if(clazz.isAnnotationPresent(MoradorAnnotation.class)){ 
                Annotation a = clazz.getAnnotation(MoradorAnnotation.class); 
                MoradorAnnotation m = (MoradorAnnotation) a; 
                System.out.printf("Data inicial de moradia: '%s'%n", m.dataInicialMoradia()); 
                System.out.printf("Inquilino: '%s'%n", m.isInquilino()); 
           } 

           for(Field attr : clazz.getDeclaredFields()){ 
                attr.setAccessible(true); 
                if(attr.isAnnotationPresent(CarroAnnotation.class)){ 
                     CarroAnnotation carroAnnotation = (CarroAnnotation) attr.getAnnotation(CarroAnnotation.class); 
                     System.out.printf("Placa: '%s'%n", carroAnnotation.placa()); 
                     System.out.printf("Numeração: '%s'%n", carroAnnotation.numeracaoDocumentoCarro()); 
                } 
           } 
      } 
 } 

Viu como é fácil ? Agora pense que todos aqueles frameworks famosos que você usa, usam essa técnica para classes anotadas. Talvez agora, aquela idéia que você tenha de que os frameworks usam códigos super complexos, não é tão real assim né.

Sugestões ?! Críticas ?! Elogios ?! 

Deixe aí nos comentários ou na nossa página do facebook.


Referência:
http://pt.wikipedia.org/wiki/Metadados
http://blog.hallanmedeiros.com/2009/06/11/criar-anotacoes-em-java/
http://www.mkyong.com/java/java-custom-annotations-example/

2 comentários:

Unknown disse...

Muito boa esta abordagem sobre como construir uma anotação, realmente nunca tinha imaginado como isso era construído.

Preciso estudar sempre disse...

Obrigado pelo comentário Bruno. De fato, a construção de anotações é muito fácil, assim como outros recursos no java. Nós, na qualidade de programadores, as vezes achamos que certos recursos são muito complicados mas, não são.