sexta-feira, 1 de maio de 2009

Reflection em Java

java_reflection

Conversando com uns amigos da pós-graduação, surgiu a pergunta: O que é reflection? Sabiamos a resposta, mas sabe quando você fica olhando um para cara do outro e acaba falando: “ah é díficil de explicar”. Então essa foi a motivação para esse post.

O que é reflection? reflection é considerado lento? vou “quebrar” o encapsulamento?

Reflection é geralmente utilizando para oferecer a opção da aplicação instanciar classes, invocar métodos e acessar atributos a partir de strings, como o nome do método que se quer invocar, passadas como parametros, fazendo com que a aplicação execute fluxos de controle mais dinâmicos e em tempo de execução.

Frameworks como o JSF e o Struts utilizam reflection para atribuir valores do request dentro das propriedades de um java bean.

Na verdade reflection é bem mais simples do que se imagina, já que ele trabalha na “execução dinâmica” de coisas triviais para os desenvolvedores: classes, métodos, atributos e etc. Basicamente o código consiste em fazer um cast de uma chamada Class.newInstance usado para criar novos objetos para a classe/interface que se deseja utilizar.

A API de Criação de Objetos disponibiliza um método static para programaticamente instânciar classes em tempo de execução fornecendo apenas o nome da classe. Basicamente utilizando Class.forName(“net.valdemarjr.blog.Classe”). Esse método é o mais comum para recuperar a instância, mas não é o único método, possuindo também Class.forName(String name, boolean initialize, ClassLoader loader).

Class reflection = Class.forName("net.valdemarjr.blog.Classe");
IFClasse classe = (IFClasse) reflection.newInstance();
System.out.println(classe.metodo());


Para Invocar métodos é obter o Class e dele você pode iterar entre o array de métodos da classe ou através da assinatura do método. E a invocação do método é através de Method.invoke();

Method[] methods = classe.getMethods();
for (Method m: methods) {
if (m.getName().equals(“metodo”)) {
return m.invoke(classe.newInstance());
}
}


Assim como classes e métodos podem ser acessados em tempo de execução acessar atributos não poderia ficar de fora. Atributos podem ser lidos e escritos em tempo de execução chamando os métodos Field.get e Field.set, classe que é recuperada apartir da instância criada Class.newInstance().

Field field = classe.getField("atributo");
System.out.println("Valor Atributo: " + field.toString());
Object instance = classe.newInstance();
field.set(instance, “novoValor”);


Um detalhe importante é que todas a regras de visibilidade(protected, default, private e etc) ainda são validas, então não adianta tentar acessar um atributo private de uma outra classe, por exemplo, você receberá um java.lang.NoSuchFieldException.

Lógico que reflection não é apenas isso, tanto que tem um livro falando só sobre reflection. Bom fica a dica aí.

3 comentários:

Jonas Abreu disse...

Olá,

talvez você se interesse por esse projeto, que facilita o trabalho com a API de Reflection do Java.

http://projetos.vidageek.net/mirror/mirror/

kiev gama disse...

Ola Valdemar,

Reflection é realmente bem interessante e bem ùtil.

Os exemplos de còdigo que vc mostra são bem claros e diretos. Entretanto, a regra de visibilidade pode ser burlada com o método Field.setAccessible (exceto se vc proibir isso com java security policies), como no exemplo que coloco abaixo. Na verdade isso não vai mudar o bytecode da classe, e o campo continuarà private. O que ele faz é alterar a acessibilidade daquela instância de Field. Caso o set seja feito novamente, mas sem efetuar o setAccessible, a exceção NoSuchFieldException serà levantada.

Segue o exemplo:
...
Test t = new Test();
Field f = t.getClass().getDeclaredField("x");
f.setAccessible(true);
/* o set vai funcionar graças à linha anterior */
f.set(t, new Integer(1));
/* abaixo, recupera-se outra instancia de field */
f = t.getClass().getDeclaredField("x");
/* tentativa de set sem mudar acessibilidade var gerar erro */
f.set(t, new Integer(1));
...

/*A classezinha de exemplo*/
class Test {
private int x;
}


Saudações,
Kiev

Valdemar Júnior disse...

@Jonas Utilizei a lib que você passou, realmente muito boa, só de não ficar lançando aquele monte de Exception, já deixa o código mais limpo. Parabéns pelo projeto, vou indicar no meu trabalho.

Postar um comentário

Postar um comentário