You are on page 1of 8

Ateno Todos os exemplos de cdigo mostrados nesse artigo necessitam do Java 1.5 beta1 ou superior para funcionar.

Para compilar, necessrios passar o parmetro -source 1.5 ao javac, como mostrado abaixo: view plainprint? 1. javac -source 1.5 NomeDaClasse.java

Caso no seja explicitamente especificado o -source 1.5, o compilador ir tentar compilar usando a verso 1.4 e dar erros de compilao nas partes onde ele no reconhecer as coisas especficas do 1.5. Aprendendo a usar Metadata no Java As utilidades das Annotations, ou Metadata, que aqui no artigo chamaremos de "atributos", so as mais variadas, que vo desde adicionar suporte a transao aos mtodos e classes, diretivas de configurao, controle de verso, validaes, logging, unit tests e por ai vai. Tais atributos, ou "metatags", so criadas pelo desenvolvedor, para qualquer coisa QUALQUER COISA que achar necessrio. Pense em XML: voc cria um documento XML usando as tags que acha serem necessrias para a situao, como <nome>: para representar algum nome, <idade> para idade, e assim por diante. E aps ter criado esse documento, ele automaticamente funciona da maneira que voc deseja, como num passe de mgica? obviamente no. necessrio escrever umas linhas de cdigo que processem esse arquivo XML, dando "vida" a cada tag. O mesmo ocorre com metadata. Voc cria uma tag da maneira que desejar, com o nome que desejar, para fazer o que bem entender. E depois escreve umas linhas de cdigo para "processarem" tais tags, para darem sentiddo sua existncia. Se voc criar uma tag para fazer logging de classes e mtodos, vai tambm precisar fazer o cdigo que buscar pelas entidades que tenham a sua recm criada tag e que faa o controle de log de alguma maneira. Como? isso VOC, o desenvolvedor, quem decide. Assim como em XML. Claro que h casos onde usar atributos no faz muito sentido, ou casos onde seria melhor usar um arquivo de configurao parte ao invs de atrelar tudo no seu cdigo-fonte. Fazer uso dessa funcionalidade sabiamente pode no ser uma tarefa to simples no comeo, quando nossas mentes ainda no conseguem entender para o que de fato elas servem. Exemplos, exemplos No caso de ser usado em transaes, imagine que voc marque determinados mtodos que necessitam de transao - ou a classe inteira, se for o caso -, e o container por sua vez consultaria o objeto, verificando se o mtodo que est sendo chamado requer a funcionalidade. Ficaria algo como view plainprint? 1. 2. 3. 4. 5. 6. ... @RequiresTransaction public void saveVendor(Vendor vendor) { // } ...

simplemente fazendo isso, ao notar verificar que o mtodo saveVendor necessita de transao, o container pode processar da seguinte maneira: view plainprint? 1. 2. 3. 4. 5. 6. 7. 8. 9. ... beginTransaction(); saveVendor(vendor); if (!transactionErrors) { commit(); } else {

10. rollback(); 11. } 12. ...

Isso evita muita duplicao de cdigo e tira a responsabilidade do desenvolvedor ter que explicitamente se preocupar com o controle transacional, nem a necessidade de configurar tudo usando arquivos XML. No exemplo que veremos mais adiante, usaremos atributos para realizar comparao dinmica entre objetos. Mas antes de prosseguir, necessrio conhecer o bsico sobre o funcionamento dos atributos. Attributos podem no ter parmetro algum, ou seja, voce usa eles apenas para "marcar" a classe, mtodo ou campo da classe; podem receber tantos quantos parmetros voc desejar; e pode ter parmetros com valores padro. Alm disso, pode ser configurado para estar disponvel somente em tempo de compilao; disponvel para a VM como um todo, ou disponvel no byte-code apenas. Para processar os atributos dinmicamente, via reflection, necessrio configur-lo para estar disponvel em RUNTIME. Veremos como fazer isso mais adiante. Criamos atributos usando a palavrachave @interface, como no exemplo abaixo: view plainprint? 1. public @interface MeuAtributo { 2. }

simples assim. Os atributos so interfaces como outras quaisquers, e o sinal de arroba ( @ ) utilizado apenas para instruir o compilador sobre a necessidade de realiziar certas operaes para "transformar" a interface em algo que seja reconhecido como uma anotao - Annotation, oficialmente. Para ns, desenvolvedores, algo transparente, no necessitando de diretivas especiais para a compilao. Para informar a visibilidade do atributo, usamos outro atributo chamado @Retention, cujo qual faz parte da linguagem. Os valores possveis para um @Retention so os do tipoRetentionPolicy, encontrado no pacote java.lang.annotation. H ainda a possibilidade de especificar onde o atributo poder ser usado, sendo que algumas possiblidades so para somente construtores, somente mtodos, package ou declarao de classe, enum ou interface. Os tipos possveis esto declarados no enum ElementType, tambm presente em java.lang.annotation Por exemplo, digamos que queremos que o nosso atributo somente possa ser usado em mtodos, e deve estar disponvel durante a execuo do programa. Para isso, bastaria fazer view plainprint? 1. 2. 3. 4. @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MeuAtributo { }

Assim, caso algum tente usar o atribuo na declarao de alguma classe, ir obter um erro de compilao, pois MeuAtributo pode ser usado somente com mtodos. Usar o nosso atributo igualmente simples, bastando colocar a chamada antes da declarao do mtodo, precedido pela arroba. Veja o exemplo: view plainprint? 1. public class MinhaClasse { 2. @MeuAtributo 3. public void fazAlgumaCoisa() { 4. } 5. }

O atributo usado como exemplo no faz muita coisa alm de "marcar" o mtodo. Atributos sem parmetros podem ser teis nos casos onde voc deseja informar que o "alvo" em questo

apto a fazer ou receber algo. Exemplo disso pode ser unit tests, que ao invs de usar uma determinada nomenclatura ao criar os mtodos a serem testados, poderamos fazer algo como view plainprint? 1. public class VendorTestCase extends TestCase { 2. @Test 3. public void criaVendor() { 4. } 5. 6. @Test 7. public void atualizaVendor() { 8. } 9. 10. private Vendor localizaVendor() { 11. // ... 12. } 13. }

Assim, os mtodos marcados com o atributo @Test sero considerados como parte do test case. Por outro lado, atributos com parmetros so teis quando precisamos passar algum tipo de informao, necessrio para a sua execuo. Para um controle de dependncia, digamos que, antes de executar determinado mtodo, voc precisa se certificar que algum outro mtodo, digamos que de configurao, tenha sido chamado, e que, ao finalizar a chamada, um outro mtodo seja executado para enviar notificao para alguma entidade. Com base nisso, poderamos pensar em fazer view plainprint? 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. public class ConfiguratorTabajara { public Config prepareSystem() { // faz qualquer coisa } } public class ClasseDeAlgumaCoisa implements Observer { @Depends(class = "ConfiguratorTabajara", method = "prepareSystem") @AfterCall(execute = "notifyAll") public void executaAlgumaFuncao() { // ... } // Envia notificacoes para outras entidades private void notifyAll(Object sender) { } }

Assim, antes do mtodo executaAlgumaFuncao ser executado, o sistema verificaria se prepareSystem j foi chamado e, caso ainda no, realiza a execuo do mesmo antes de prosseguir. Aps a finalizao da chamada do nosso mtodo, o sistema automaticamente executaria o mtodonotifyAll, passando a instncia do objeto como parmetro. Claro que isso tudo no acontece por mgica, como veremos no exemplo logo abaixo. Para esses passos todos poderem acontecer, necessrio que as chamadas aos mtodos sejam feitas por alguma outra entidade, como um Container, Proxy ou Interceptador, ou explicitamente pelo prprio desenvolvedor. Atributos que recebem parmetros so declarador quase da mesma forma como declaramos interfaces, sendo que h uma pequena diferena possvel. Veja o exemplo: view plainprint?

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.

// Depends.java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Depends { String class(); String method(); } // AfterCall.java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AfterCall { String execute(); }

como voc pde notar, os nomes dos parmetros que usamos na chamada ao atributo so mtodos comuns. A diferena, uma possibilidade adicional, especifcar um valor padro para as propriedades dos atributos. No caso do atributo @Test poderamos fornecer uma propriedade para especificar se para realizar o "test" ou no, sendo que o valor padro seria true. A declarao ficaria como no exemplo abaixo: view plainprint? 1. 2. 3. 4. 5. 6. // Test.java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Test { boolean value() default true; }

Assim, poderamos usar tanto view plainprint? 1. 2. 3. 4. 5. ... @Test public void criaVendor() { } ...

quanto view plainprint? 1. 2. 3. 4. 5. ... @Test(false) public void criaVendor() { } ...

Todas essas chamadas so vlidas. Caso quisssemos usar algum valor padro do tipo String, bastaria colocar o valor entre aspas, como em qualquer outra declarao de String em Java. Agora que j vimos vrios exemplos de como criar e usar atributos, vamos ver o exemplo final, onde ser mostrado como verificar, dinamicamente, quais mtodos contem atributos, e realizar operaes com base nisso. O exemplo mostrado ser um programa que compara duas instncias, retornando os nomes dos mtodo cujo retorno no seja igual ao retorno da outra instncia. Primeiramente vamos criar o atributo e a classe que usaremos para comparar suas

instncias. Logo aps veremos como consultar atributos dinamicamente e finalizaremos fazendo o cdigo que faz as comparaes em si. Compare.java view plainprint? 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.ElementType.METHOD; @Retention(RUNTIME) @Target(METHOD) public @interface Compare { boolean value() default true; }

Note que usamos uma outra funcionalidade do Java 1.5, que so os static imports. Prosseguindo, temos a nossa classe bsica: DummyObject.java view plainprint? 1. public class DummyObject { 2. private String name; 3. private String email; 4. private int age; 5. 6. public DummyObject(String name, String email, int age) { 7. this.name = name; 8. this.email = email; 9. this.age = age; 10. } 11. 12. @Compare 13. public String getName() { 14. return this.name; 15. } 16. 17. @Compare(false) 18. public String getEmail() { 19. return this.email; 20. } 21. 22. @Compare 23. public int getAge() { 24. return this.age; 25. } 26. 27. public void something() { 28. } 29. }

Veja que o mtodo getEmail recebeu false como parmetro, e o mtodo something no foi marcado como comparvel. Ao executar o exemplo, isso ser til para mostrar a busca por mtodos que contm o atributo em questo e como pegar o valor configurado para ele. O prximo passo fazer o mtodo que ir receber as instncias e buscar por mtodos com atributos. Apesar de ser um pouco mais complexa que as classes vistas at agora, bastante simples de entender mesmo assim. ObjectComparator.java view plainprint?

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.

import java.util.List; import java.util.ArrayList; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.lang.annotation.Annotation; public class ObjectComparator { private List<Method> findAnnotatedMethods(Class clazz) { // Pega todos os mtodos da classe Method[] methods = clazz.getDeclaredMethods(); List<Method> list = new ArrayList<Method>(); // Verifica cada mtodo, procurando por Annotations for (Method method : methods) { // Verifica se o mtodo atual contm o nosso atributo if (method.isAnnotationPresent(Compare.class)) { Annotation annotation = method.getAnnotation(Compare.class); // Joga a annotation no console, para vermos o seu valor System.out.printf("Mtodo: %s Annotation: %s\\n", method.getName(), annotation); if (((Compare)annotation).value()) { list.add(method); } } }

22. 23. 24. 25. 26. 27. 28. return list; 29. } 30. }

Ao bater o olho, possvel ver outras duas novas funcionalidades do Java 1.5: Generics e foreach loop ( ou enhanced for loop, como tambm chamado ). A declarao view plainprint? 1. List<Method> list = new ArrayList<Method>();

declarada uma lista que somente pode receber objetos do tipo Method, facilitando assim a manipulao, j que evita vrios casts. Esse um exemplo do uso mais simples que Generics nos proporcionam. Por sua vez, a linha view plainprint? 1. for (Method method : methods) {

traduzida como "para cada elemento do tipo Method contido na varivel methods, crie uma varivel local chamada method". Essa linha a mesma coisa que fazer view plainprint? 1. for (int i = 0; i < methods.lenth; i++) { 2. Method method = methods[i];

em verses do Java anteriores ao 1.5. Voltando ao nosso exemplo, na linha view plainprint?

1. if (method.isAnnotationPresent(Compare.class)) {

verificamos se o mtodo atual contm o atributo do tipo Compare. Como voc percebeu, o mtodo isAnnotationPresent recebe o "Class", ou "type", que queremos verificar. Por fim, em view plainprint? 1. if (((Compare)annotation).value()) {

chamamos o mtodo needCompare, declarado no Objeto Comparer, para verificar se o mtodo para ser usado nas comparaes. A prxima e penltima parte do nosso programa executar a chamada ao mtodo que consulta os atributos, e, via Reflection, comparar as instncias. Adicione o seguinte mtodo classe ObjectComparator: view plainprint? 1. private List<String> compareInstances(Object original, Object toCompare) { 2. // Pega os mtodos que contm o atributo de comparao 3. List<Method> annotatedMethods = this.findAnnotatedMethods(original.getClass()); 4. List<String> differences = new ArrayList<String>(); 5. 6. try { 7. // Compara cada retorno de objeto 8. for (Method m : annotatedMethods) { 9. Object value = m.invoke(original, null); 10. 11. Method toCompareMethod = toCompare.getClass().getMethod(m.getName()); 12. if (!toCompareMethod.invoke(toCompare, null).equals(value)) { 13. differences.add(m.getName()); 14. } 15. } 16. } 17. catch (Exception e) { 18. e.printStackTrace(); 19. } 20. 21. return differences; 22. }

Nesse mtodo tambm no h muito segredo. Ele simplesmente pega os mtodos a serem comparados, itera por cada um, executando uma chamada dinmica para comparar o valor retornado pelo mtodo do objeto "original" com o retorno do do mesmo mtodo do objeto a ser comparado. Por fim, retorna uma lista contendo os possveis nomes de mtodos cujo retorno foi diferente. Para finalizar, basta fazermos a classe main() para invocar o teste. Adicione o seguinte mtodo na classe ObjectComparator: view plainprint? 1. public static void main(String[] args) 2. { 3. DummyObject d1 = new DummyObject("Nome 1", "nome@um", 13); 4. DummyObject d2 = new DummyObject("Nome 2", "nome@dois", 13); 5. 6. List<String> diff = new ObjectComparator().compareInstances(d1, d2); 7. System.out.println("Objetos iguais? "+ (diff.size() == 0)); 8. 9. if (diff.size() > 0) { 10. System.out.println("Mtodos cujo retorno no so iguais: ");

11. 12. for (String methodName : diff) { 13. System.out.println(methodName); 14. } 15. } 16. }

Execute o programa e veja o resultado. Usando o exemplo acima, ser mostrando que os objetos no so iguais, e que o mtodo getName() diferente entre as duas instncias. Tente colocar valores iguais em ambos objetos, e veja o resultado. Isso finaliza o artigo sobre Metadata / Annotations / Atributos. H muita coisa til e legal que possvel fazer com essa funcionalidade, uma das mais legais do Java 1.5. Usado com sabedoria, ir facilitar muito o diaa-dia e aumentar a produtividade. Para maiores informaes e exemplos, consulte:http://jcp.org/aboutJava/communityprocess/review/jsr175/index.html http://java.sun.co m/j2se/1.5.0/docs/api/java/lang/annotation/packagesummary.html http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/packagesummary.html E, claro, no deixe de discutir sobre o assunto no frum do GUJ. At a prxima.

You might also like