Java e seus espelhos: Reflexão Computacional
Hoje vou abordar um tema bem interessante: Reflexão Computacional e como o Java trata tal assunto.
Quem se diz “programador” e nunca ouviu falar disso tem que rever seus conceitos. Existe toda uma filosofia por trás do conceito de Reflexão Computacional (ou apenas reflexão), mas eu não vou falar muito sobre isso (até porque eu não sei muita coisa sobre a teoria…
), vamos mais a prática.
Bom, vamos viajar um pouquinho: imagine que vc tem a missão de construir um método que receba um objeto que você não sabe qual é, e tenha que aprender a usá-lo em tempo de execução, ou seja, descobrir quais métodos esse objeto possui, quais variáveis de instância e etc (CALMA! Eu sei que parece besteira e que vc deve estar pensando: “Onde diabos eu vou usar um troço desses?!?” mas CALMA, vou dar uns exemplos mais adiante e em outros posts).
Como Java é uma linguagem quase completamente orientada a objetos, presume-se que TUDO é um objeto: um método é representado por um objeto da classe java.lang.reflect.Method, toda variável de instância também é representada um objeto da classe java.lang.reflect.Field e uma classe (qualquer uma delas, seja String, Integer, Float, ou aquela que vc escreveu) é representado por um objeto genérico: o objeto Class<?>.
Chega de historinha, vamos escrever alguma coisa para demonstrar tudo isso. Vamos criar uma classe com algumas variáveis e alguns métodos bem inúteis, só para exemplificar.
public class Contato { private String nome; private int idade; private String email; public Contato() { } public Contato(String nome, int idade, String email) { this.nome = nome; this.idade = idade; this.email = email; } public void emString() { System.out.println(nome + " - " + idade + " - " + email); } public int idadeEmDias() { return idade * 365; } public void enviarEmail(String assunto, String mensagem) { System.out.println("Email"); System.out.println("Para: " + email); System.out.println("Assunto: " + assunto); System.out.println("Mensagem: " + mensagem); System.out.print("Enviando..."); System.out.println("concluído"); } }
Como da pra ver… uma classe bem simples e que não faz absolutamente nada.
Vamos ao código de reflexão que depois eu explico ele:
public class Reflexao{ public Reflexao(){ Contato c = new Contato("Felipe", 20, "fesaab@gmail.com"); infoClasse(c.getClass()); } public void infoClasse(Class<?> classe){ //percorrendo as variáveis de instancia for (Field f : classe.getDeclaredFields()){ System.out.println(f.getName()); } //percorrendo os métodos for (Method m : classe.getDeclaredMethods()){ System.out.print(m.getReturnType().getName()+" "); System.out.print(m.getName()+" "); Class<?> c[] = m.getParameterTypes(); if (c.length == 0){ System.out.println("()"); }else{ System.out.print("("); } for (Class<?> cl : c){ System.out.print(cl.getName()+","); } System.out.println(")"); } } }
Muito bem, logo no construtor da classe Reflexao foi criado o objeto “c” da classe Contato e logo em seguida foi invocado o método “infoClasse(Class<?>)” passando como parâmetro um objeto da classe “Class<?>” que for retornado pelo método “getClass()” que QUALQUER objeto tem.
No método infoClasse(..), a primeira instução for percorre todas as variáveis de instância da classe passada no parâmetro.
A segunda instrução for percorre todos os métodos da classe. Eu dei uma “enfeitada” nesse segundo for, mais foi só para demonstrar alguns métodos da classe Method. A execução do código gera a seguinte saída:
nome idade email void emString () int idadeEmDias () void enviarEmail (java.lang.String,java.lang.String,)
Repare que apenas foram exibidas informações da CLASSE, nada do objeto foi revelado. Para tal propósito, devemos explorar alguns outros métodos, vamos ao código:
public void infoObjeto(Object o) throws Exception{ Class<?> classe = o.getClass(); for (Field f :classe.getDeclaredFields()){ f.setAccessible(true); System.out.println(f.getName()+": "+f.get(o)); } }
Para descobrir o conteúdo das variável de instância, deve-se:
- Recuperar o objeto Field da respectiva variável
- Se a variável não possuir visibilidade pública (como é o caso das nossas, são privadas), deve-se liberar o acesso para seu conteúdo através do método setAccessible(true).
- Recuperar o conteúdo da variável através do método get(Object) da classe Field. Lembrando que o método pertence ao objeto da classe Field, esse objeto não sabe o valor da variável de instância que agente quer, mas ele sabe como recuperar esse valor porque esse objeto representa a variável. Portanto agente simplesmente fala assim: “ô objeto da classe Field, recupera o valor da variável que você representa. A variável que eu quero está nesse objeto aqui”. Por isso agente passa o objeto como parâmetro para o método.
A execução do método infoObjeto(…) gera a seguinte saída:
nome: Felipe idade: 20 email: fesaab@gmail.com
Antes de finalizar essa pequena introdução, só mais uma coisa: eu mostrei como recuperar os objetos Field e Method que representam as variáveis de instância e os métodos de um objeto de uma classe qualquer, mostrei também como recuperar o conteúdo de qualquer variável de instância, só falta ver como agente executa um método.
É extramente simples… uma vez que agente já sabe como recuperar os objetos Method que representam os métodos, basta chamar o método invoke(Object, Object[]).
O primeiro parâmetro é o objeto que agente quer que execute o método, e o segundo é um vetor que contém os parâmetros que o método exige. Vamos a um exemplo:
public void executaMetodos(Object o) throws Exception { Class<?> classe = o.getClass(); for (Method m : classe.getDeclaredMethods()) { if (m.getParameterTypes().length == 0) { System.out.println("Invocando o método: " + m.getName()); if (m.getReturnType().getName().equals("void")) { m.invoke(o, new Object[0]); } else { System.out.println(m.invoke(o, new Object[0])); } } } }
Só para simplificar eu restringi a invocar apenas os métodos que não tem parâmetro nenhum para passar, se tivesse, bastava descobrir através do método “getParameterTypes()” da classe Method e criar o array de Object com os parametros corretos.
No código eu também separei a chamada de métodos que não tem retorno dos que tem utilizando o método “getReturnType()”, quando tiver retorno eu simplesmente imprimo ele na tela. O resultado da execução do código é o seguinte:
Invocando o método: emString Felipe - 20 - fesaab@gmail.com Invocando o método: idadeEmDias 7300
Bom galera, essa foi uma pequena introdução sobre reflexão computacional. Espero que ajude um pouco quem estava procurando sobre o assunto.
Nos próximos posts eu vou utilizar esse conceito numa abordagem bem mais próxima da realidade (onde usar isso).
[] e até a próxima,
Saab.
One Response so far
Ricardo Borba
janeiro 3rd, 2012
9:06
Muito legal, parabéns.
Leave a comment