Sunteți pe pagina 1din 10

Anotao com Reflection em Java

Reflection
Ao desenvolver um software conseguimos definir diversos recursos de forma esttica, como usar
mtodos de classes j criada, definir atributos tambm previamente criados e etc. Na maioria das
vezes, isso suficiente para a construo de um projeto, mas h excees e exatamente elas
que estudaremos.
Pense em como possvel o framework Hibernate saber que nossa classe Cliente tem um
mtodo setNome() se na codificao dele com certeza no h nenhuma referncia a esta classe?
Lembre-se que os desenvolvedores do Hibernate ou qualquer outro framework jamais
imaginaram que voc precisaria usar a classe Cliente. Eles desenvolveram tal recurso da forma
mais genrica possvel afim de adaptar-se a qualquer regra de negcio.
A resposta para esta pergunta : Reflection, j que seu uso comumente aplicado a muitos
frameworks afim de tornar o mesmo plugvel, tais como: Hibernate, Spring, CDI e etc. O pacote
javax.reflection possibilita que sejam feitas chamadas a mtodos, atributos e etc. em tempo de
execuo, ou seja, conseguimos instanciar uma classe sem saber qual a classe. Mas como isso
possvel?
Primeiro vamos construir um Java Bean que ser utilizada durante todo nosso artigo com uma
classe simples chamada Cliente com seus atributos e mtodos de acesso (getters e setters),
como mostra a Listagem 1.
Listagem 1. Classe Cliente
import java.util.Date;

public class Cliente {


private String nome;
private int codigo;
private Date dataNascimento;
private String nomePai;
private String nomeMae;
private String enderecoCompleto;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public int getCodigo() {
return codigo;
}
public void setCodigo(int codigo) {
this.codigo = codigo;
}
public Date getDataNascimento() {
return dataNascimento;
1

}
public void setDataNascimento(Date dataNascimento) {
this.dataNascimento = dataNascimento;
}
public String getNomePai() {
return nomePai;
}
public void setNomePai(String nomePai) {
this.nomePai = nomePai;
}
public String getNomeMae() {
return nomeMae;
}
public void setNomeMae(String nomeMae) {
this.nomeMae = nomeMae;
}
public String getEnderecoCompleto() {
return enderecoCompleto;
}
public void setEnderecoCompleto(String enderecoCompleto) {
this.enderecoCompleto = enderecoCompleto;
}
}
So trs os recursos que o reflection possibilita que acessar em tempo de execuo: Class, Field
e Method. Com estes trs voc consegue fazer tudo que precisa com qualquer tipo de classe.
Primeiro vamos aprender um pouco sobre os mtodos que o reflection fornece e depois usaremos
como exemplo o bean criado na Listagem 1 para resolver problemas reais. Observe a Listagem
2.
Listagem 2. Usando getClass()
public static void main(String[] args) {
Cliente cliente = new Cliente();
System.out.println(cliente.getClass().getName());
}
Sada: Cliente
O primeiro mtodo o getClass() e com ele ns capturamos a classe do objeto cliente, que no
nosso caso a classe Cliente. O getClass() retorna um objeto Class, que possui muitos mtodos
para manipularmos a classe Cliente, tais como: getName(), getModifiers, getConstructor e etc.
Usamos acima um objeto para retornar a classe dele, mas poderamos usar a prpria classe
Cliente para obter essas informaes, da mesma forma que a apresentada na Listagem 3.
Listagem 3. Usando Cliente.class
public static void main(String[] args) {
Cliente cliente = new Cliente();
System.out.println(Cliente.class.getName());
2

}
Com a Class em mos podemos comear a destrinchar os recursos contidos dentro dela como,
por exemplo: atributos, mtodos, modificadores, construtores e etc. Vamos ver o nome de todos
os atributos na Listagem 4.
Listagem 4. Capturando o nome dos atributos da classe Cliente
public static void main(String[] args) {
Cliente cliente = new Cliente();
Class<Cliente> clazz = (Class<Cliente>) cliente.getClass();
for(Field f : clazz.getDeclaredFields()){
System.out.println(f.getName());
}
}
Sada:
nome
codigo
dataNascimento
nomePai
nomeMae
enderecoCompleto
O mtodo getDeclaredFields() retorna um array de Field, onde Field a classe utilizada para
manipular os atributos presentes na classe que estamos trabalhando. Podemos aplicar a mesma
lgica para os mtodos, como mostra a Listagem 5.
Listagem 5. Capturando o nome dos mtodos da classe Cliente
public static void main(String[] args) {
Cliente cliente = new Cliente();
Class<Cliente> clazz = (Class<Cliente>) cliente.getClass();
for(Method m : clazz.getDeclaredMethods()){
System.out.println(m.getName());
}
}
Sada:
getNome
setNome
getCodigo
setCodigo
getDataNascimento
setDataNascimento
getNomePai
setNomePai
getNomeMae
setNomeMae
getEnderecoCompleto
setEnderecoCompleto
3

Como poderamos criar um mtodo genrico para instanciar/criar todo tipo de objeto,
independente da sua classe? Imagine que ns no saibamos qual a classe que deve-se
instanciar, ento no podemos usar a palavra reservada new MinhaClass(). Precisamos apenas
disponibilizar um mtodo onde seja passada a classe, atravs do MinhaClasse.class, e neste
mtodo seja feita a instanciao e o retorno seja o objeto desejado. Observe a Listagem 6.
Listagem 6. Criando um mtodo genrico para instanciar Classes com reflection
private static Object createNewInstance(Class clazz) {
Constructor<?> ctor;
try {
ctor = clazz.getConstructors()[0];
Object object = ctor.newInstance();
return object;
} catch (SecurityException
| InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
O mtodo createNewInstance responsvel por retornar uma instncia de Class, independente
do seu tipo. O que fazemos utilizar o clazz para capturar o primeiro construtor encontrado, que
o vazio. Aps capturar este ns chamamos o mtodo newInstance(), que retorna um objeto do
tipo clazz. Algumas excees so obrigatrias, por isso colocamos o bloco try-catch e
adicionamos cinco excees requeridas, ou checked exceptions.
Vejamos como utilizar o mtodo disposto na Listagem 6 com o cdigo da Listagem 7.
Listagem 7. Usando o createnewinstance
public static void main(String[] args) {
Cliente cliente = (Cliente) createNewInstance(Cliente.class);
if (cliente == null) {
System.err.println("Ops, no foi possvel criar o objeto cliente");
} else {
System.out.println("Objeto cliente criado = " + cliente.toString());
}
}
Sada:
Objeto cliente criado = Cliente@5f67198e
Observe que em nenhum momento usamos new Cliente(), pois o mtodo createnewinstance
nem sabe que a classe Cliente existe. Ele s saber disso em tempo de execuo.
Agora fica mais claro entender como os frameworks atuais conseguem ler a sua classe sem ter
digitado uma linha de cdigo sobre ela, repare que nada foi implementado especificamente para
classe Cliente.
4

Anotaes
Anotaes so metadados, disponibilizados a partir do Java 5, para configurar determinados
recursos que antes deveriam ser feitos em arquivos separados como, por exemplo, no XML.
Voc diariamente deve usar diversas anotaes, tais como @Override, @Deprecated, @Entity,
@Table, @Column e etc. Se voc tenta usar o @Override em um mtodo que no tem um
semelhante na classe pai, ento voc ver um erro em tempo de design, e isso s possvel
porque o Java usa o reflection para checar se existe um mtodo com a mesma assinatura na
classe pai, caso contrrio, o @Override no ser aceito.
Nesta seo vamos criar nossa prpria anotao, que ter por objetivo anotar os mtodos que
devem ser mostrados no console, assim entenderemos como funciona a criao de anotaes.
Para no confundir comearemos anotando nossos mtodos com @Mostrar, como mostra a
Listagem 8.
Listagem 8. Anotando nossos mtodos com @Mostrar
import java.util.Date;

public class Cliente {


private String nome;
private int codigo;
private Date dataNascimento;
private String nomePai;
private String nomeMae;
private String enderecoCompleto;
@Mostrar
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}

@Mostrar
public int getCodigo() {
return codigo;
}
public void setCodigo(int codigo) {
this.codigo = codigo;
}
@Mostrar
public Date getDataNascimento() {
return dataNascimento;
}
5

public void setDataNascimento(Date dataNascimento) {


this.dataNascimento = dataNascimento;
}
public String getNomePai() {
return nomePai;
}
public void setNomePai(String nomePai) {
this.nomePai = nomePai;
}
public String getNomeMae() {
return nomeMae;
}
public void setNomeMae(String nomeMae) {
this.nomeMae = nomeMae;
}

@Mostrar
public String getEnderecoCompleto() {
return enderecoCompleto;
}
public void setEnderecoCompleto(String enderecoCompleto) {
this.enderecoCompleto = enderecoCompleto;
}
}
Inicialmente voc ver o seguinte erro:Mostrar cannot be resolved to a type. Isso ocorre porque
nossa anotao no foi criada ainda e para fazermos isso seguimos o cdigo a seguir:
public @interface Mostrar {
}
A partir do momento que a anotao Mostrar for criada o erro da Listagem 8 desaparecer e
voc conseguir compilar o cdigo. O Java optou por usar @interface como recurso para
anotaes, pois os arquitetos da linguagem preferiram no criar uma nova palavra reservada
apenas para anotao, algo como: public Annotation Mostrar{}.
Agora precisamos definir dois atributos importantes para nossa anotao:
1. Que tipo de estrutura ela pode anotar? Mtodos, Classes, atributos, construtores, pacotes
e
etc.?
Para isso usamos @Target(ElementType.METHOD) quando desejamos especificar que
nossa anotao servir apenas para mtodos, ou @Taget(ElementType.FIELD) para
anotar atributos, e assim por diante.
6

2. A anotao serve apenas em tempo de compilao ou execuo? Um exemplo disto a


anotao @Override, que serve apenas em tempo de compilao, pois o Java ir checar
se aquele mtodo existe na classe pai, caso contrrio, o cdigo nem chegar a ser
compilado. J a nossa anotao @Mostrar ser usada apenas em tempo de execuo,
pois quando estivermos lendo os mtodos da classe Cliente verificaremos se este foi
anotado com @Mostrar, caso contrrio, ignoraremos ele. Para isso usaremos:
@Retention(RetentionPolicy.RUNTIME).
Veja como ficou nossa anotao final na Listagem 9.
Listagem 9. Anotao @Mostrar completa
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Mostrar {
}
Agora sim estamos prontos para o ltimo passo, que fazer uso da anotao @Mostrar com o
reflection. Nosso objetivo ser popular o objeto cliente com alguns dados e depois pass-lo para
um mtodo onde os valores anotados com @Mostrar sero mostrados no console. Por exemplo,
o getNome() mostrar o nome do cliente e assim por diante, como mostra a Listagem 10.
Listagem 10. Mostrando mtodo com anotaes @Mostrar
// Mostra valores apenas com anotao @Mostrar
public static void mostrarValores(Object obj) {
try {
Class clazz = obj.getClass();
for (Method m : clazz.getDeclaredMethods()) {
if (m.isAnnotationPresent(Mostrar.class)){
System.out.println(m.getName()+": "+m.invoke(obj));
}
}
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
O mtodo mostrarValores() recebe o objeto que queremos manipular. Logo em seguida,
capturamos a classe desse objeto. De posse da classe ns capturamos todos os mtodos desse
objeto, pois sabemos que a anotao @Mostrar s pode ser usada em mtodos.
Fazendo uma iterao nos mtodos do objeto passado, ns precisamos verificar se aquele
determinado mtodo possui a anotao @Mostrar, e para fazer isso usamos o seguinte cdigo:
7

m.isAnnotationPresent(Mostrar.class)
O isAnnotationPresent() verifica se aquele determinado mtodo possui a anotao passada como
parmetro, no nosso caso, Mostrar.class. Se isso for verdade, podemos executar o mtodo. Mas
como fazemos isso?
Uma ao muito interessante da classe Method o invoke(), que lhe possibilita chamar um
mtodo atravs do reflection, e exatamente ele que usamos para chamar o getNome(),
getCodigo() e etc., como mostra o cdigo a seguir:
System.out.println(m.getName()+": "+m.invoke(obj));
O invoke() retorna um object, por ser a classe de mais alta hierarquia, e podemos fazer cast para
qualquer outra. Isso significa que nosso mtodo pode retornar um inteiro, double, string, char, list,
set e etc.
Conseguiu entender o que ir acontecer aps chamarmos o invoke()? Apenas os valores
marcados com @Mostrar sero mostrados no console, assim como desejamos.
Vamos ver um exemplo prtico disto na Listagem 11.
Listagem 11. Usando o mtodo mostrarValores()
public static void main(String[] args) {
Cliente cliente = new Cliente();
cliente.setCodigo(1010);
cliente.setDataNascimento(new Date());
cliente.setEnderecoCompleto("Rua ABC, Bairro YHU n 190");
cliente.setNome("Antonio da Silva Nunes");
cliente.setNomeMae("Maria da Silva Nunes");
cliente.setNomePai("Joao da Silva Nunes");
mostrarValores(cliente);
}
Sada:
getNome: Antonio da Silva Nunes
getCodigo: 1010
getDataNascimento: Thu Mar 12 21:04:33 BRT 2015
getEnderecoCompleto: Rua ABC, Bairro YHU n 190
Imagine o mundo de possibilidades que se abre quando aprendemos a usar o reflection,
principalmente para quem quer trabalhar com reusabilidade em larga escala, construindo API's
responsveis por acoplar-se em qualquer projeto.
Vale atentar que dada nossa lgica apresentada, se anotarmos um mtodo set() com o @Mostrar
teremos um erro, pois o mtodo set() espera um parmetro e ns no passamos nenhum
parmetro no invoke(), como mostra a Listagem 12.
Listagem 12. Anotando o mtodo errado
@Mostrar
8

public void setNome(String nome) {


this.nome = nome;
}
Anotamos acima o setNome() com o @Mostrar, e agora vamos executar novamente a Listagem
12 e ver o resultado na Listagem 13.
Listagem 13. Resultado do @Mostrar
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at ReflectionApp.mostrarValores(ReflectionApp.java:28)
at ReflectionApp.main(ReflectionApp.java:19)
Exatamente o que espervamos: wrong number of arguments.
Neste caso, o invoke() precisa receber o parmetro para o setNome() e como no est
recebendo, ele retorna o erro acima. E como poderamos resolver isso?
Uma das formas possveis checar no mtodo mostrarValores() se o mtodo que possui a
anotao @Mostrar no recebe nenhum parmetro, caso contrrio, vamos mostrar uma
mensagem no console e passar para o prximo. Dessa forma, mesmo que seja feita uma
anotao errada no teremos muitos problemas, como mostra a Listagem 14.
Listagem 14. Ignorando mtodos com parmetros
// Mostra valores apenas com anotao @Mostrar
public static void mostrarValores(Object obj) {
try {
Class clazz = obj.getClass();
for (Method m : clazz.getDeclaredMethods()) {
if (m.isAnnotationPresent(Mostrar.class)){
if (m.getParameterTypes().length > 0){
System.err.println(" "+m.getName()+" anotado com @Mostrar de forma
errada, ignorando ...");
continue;
}
System.out.println(m.getName()+": "+m.invoke(obj));
}
}
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Adicionamos a linha if (m.getParameterTypes().length > 0) que ir verificar se existe pelo menos


um parmetro neste mtodo, e caso isso seja verdade, uma mensagem ser mostrada e a
iterao ir para o prximo passo atravs do continue.
Este artigo teve como principal objetivo mostrar a criao de uma anotao simples para mostrar
valores especficos de um objeto, mas que por necessidade foi necessrio explicar todo o
conceito de reflection at podermos chegar nas anotaes, caso contrrio, isso no seria
possvel. O uso de anotaes e consequentemente reflections est diretamente ligada a
construo principalmente de frameworks que podemos trabalhar de forma mais genrica
possvel, sem se preocupar especificamente com a regra de negcio do desenvolvedor, mas sim
com a estrutura que ele precisar

10

S-ar putea să vă placă și