Ok, o título deste post ficou meio estranho, mas como muita gente diz que isto é magia negra mesmo, então até que o título não esta tão ruim ![]()
Uma coisa que eu vejo bastante por ai, e não é de hoje, é que grande parte dos programadores Java não faz idéia do que seja Reflection, e normalmente tem medo de escrever, ou até mesmo de ler código “complicado”.
Não vou dizer que reflection é simples, mas é um recurso extremamente poderoso do Java que todo programador Java deveria conhecer.
Reflection em java, é como o nome diz, a possibilidade de programaticamente, visualizar um reflexo de um objeto ou uma classe, e como em um espelho, é possível também distorcer um pouco esta imagem quando necessário.
Como no reflexo em um espelho, o que você visualiza, não é o objeto real, apenas um reflexo deste, mas como na física, você pode deduzir como interagir com o objeto real, utilizando o seu reflexo.
Quando eu escrevi isto, lembrei de uma cena de um filme muito velho, acho que era “fúria de titâs” ou algo assim, onde alguem utilizava o reflexo da medusa em um escudo para lutar com ela sem se transformar em pedra.
Mas voltando ao assunto, reflection, é a possibilidade, de em tempo de execução, diversas informações sobre um objeto qualquer, incluindo mas não se limitando a seguinte lista:
Via reflexão também é possível por exemplo, executar as seguintes ações em um objeto de uma classe que não existia no momento em que o código foi desenvolvido (um plugin por exemplo):
Claro que estes são só exemplos, a API de reflection adiciona muito mais flexibilidade do que isto, principalmente quando combinada com algum framework de AOP ou com a API de criação de Proxies disponível no próprio Java.
Mas para fazer tudo isto, é necessário conhecer algumas classes que a maior parte dos programadores Java não se preocupam em conhecer.
Só um detalhe antes de apresentar as novas classes, apenas combinando a API de reflection com a API de Proxies e o suporte a annotations do Java 5 é possível implementar todos os recursos do EJB3 por exemplo.
Agora vamos aprender a usar espelhos para fazer mágica
Agora um exemplo básico, por que acredito que vocês não conseguiram entender muita coisa até aqui, mas eu prometo que depois de um exemplo as coisas vão ficar um pouco mais claras.
Este exemplo, razoavelmente simples, vai listar todos os métodos e atributos públicos em uma classe.
Para o exemplo se tornar um pouco mais divertido, o nome da classe deve ser passado como parâmetro, isto também faz você poder listar uma classe que não existia quando o programa foi compilado.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package test; import java.lang.reflect.*; public class List { public static void main(String[] args) throws Exception{ if(args.length!=1) throw new Exception("Voce precisa informar o nome da classe como unico parametro"); Class<?> clazz = Class.forName(args[0]); printClassInformation(clazz); printClassAttributes(clazz); printClassMethods(clazz); } public static void printClassInformation(Class<?> clazz) throws Exception { System.out.println("Class Name: " + clazz.getName()); } public static void printClassAttributes(Class<?> clazz) throws Exception { for(Field f : clazz.getDeclaredFields()){ System.out.format("\t--Private Attribute Name: %s, Attribute Type: %s\n",f.getName(),f.getType().getName()); } } public static void printClassMethods(Class<?> clazz) throws Exception { for(Method m : clazz.getMethods()){ System.out.format("\tMethod Name: %s, Return Type: %s, Parameter Types: %s\n",m.getName(),m.getReturnType().getName(),m.getParameterTypes().toString()); } } } |
Compile este exemplo, e execute passando por exemplo “java.lang.Class” como parâmetro e você vai ter um exemplo básico do funcionamento da API de Reflection.
Você pode também criar outra classe, empacotar ela em um arquivo .jar, adicionar este jar no classpath e executar este exemplo passando o nome da sua nova classe como parâmetro.
Claro que este é um exemplo extremamente básico, com um código que se não fosse pelo parâmetro > passado para algumas classes, poderia ser executado até no Java 1.2, ou seja, a API de reflexão não é nova, é apenas sub utilizada pelo programador de nível médio.
Ahh, mas saber isto vai fazer com que eu seja um expert em java?
Claro que não! Mas não saber isto, com certeza te impede de ser um
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package test; import java.lang.reflect.*; public class Call { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName(args[0]); Method m = clazz.getMethod(args[1]); Object instance = clazz.newInstance(); Object result = m.invoke(instance); System.out.println(result); } } |
Este é outro exemplo bastante simples, apenas para demonstrar algumas possibilidades, este exemplo bastante simples, chama um método sem parâmetros em uma classe qualquer que tenha um construtor padrão.
Para chamar métodos estáticos, seria necessária uma pequena alteração, o parâmetro passado para o método “invoke” da classe Method, é a instancia do objeto onde o método deve ser chamado, para métodos estáticos, esta instância é substituida pela classe que possui o método estático.
E o construtor padrão é necessário, por que precisamos de uma instância da classe para invocar o método, se a classe não possuir um construtor padrão, precisaremos passar parâmetros para o construtor, o que iria complicar bastante o exemplo, e a idéia aqui é só mostrar algumas possibilidades, e não complicar mais ainda a vida de vocês.
Mas como funciona o exemplo?
Tente compilar o exemplo, e executar ele passando os parâmetros:
java.lang.Object hashCode
ou
java.lang.Object toString
ou
QualquerNomeDeClasse nomeDeUmMétodoSemParâmetrosDestaClasse
e pronto, método executado.
Ai você vai pensar agora: Mas é muito mais fácil eu escrever direto “System.out.println(new Object().hashCode())” no meu código.
Claro que é, mas para isto você precisaria saber que o método que seria executado era o hashCode de uma nova instância de Object.
A idéia da API de reflection é obter informações em tempo de execução, é possibilitar um pouco de méta programação no Java.
Imagine só, criar um proxy para uma interface que garante que todos os métodos executados, caso tenham a anotação @Transactional, serão executados dentro do contexto de uma transação.
Isto é meta programação, isto é manter a mente um pouco mais aberta do que o programador médio.
Outra possível pergunta: Tu não vai ser expulso do clubinho por que esta revelando os segredos, como aconteceu com o Mister M?
Resposta: Claro que não, isto não é segredo nenhum, tu só não tinha aprendido antes por que não parou para estudar. Se você fosse um pouco mais preguiçoso, como eu, você já teria parado para estudar uma forma de trabalhar menos com as ferramentas que você tem na mão, e se você é um programador, meta programação, é uma forma de fazer mais trabalhando menos.
Mais uma pergunta: Por que ninguem me contou isto antes? explicando assim até parece fácil!
Resposta: Provavelmente achavam que tu é burro demais para entender, agora tu pode provar que isto não é verdade. E não se engane, não é tão simples assim, código usando reflexão pode ficar bastante complicado, eu só mostrei uns exemplos bem básicos.
A API de Proxies eu vou deixar como assunto para um próximo post, minha imagina imaginação esta meio fraca hoje, estou de saco cheio de assistir esta aula maluca que eu não to nem prestando atenção, acho que vou pra casa já
Se vocês tiverem idéias de mais exemplos que vocês querem ver como pode ser feito com reflexão, ou se tiverem perguntas sobre reflexão em java, por favor sintam-se a vontade de registrar as perguntas, dúvidas e sugestões aqui nos comentários do blog, vou tentar responder todas as perguntas ![]()
E como sempre, se você gostou deste post, indique para seus amigos, e coloque um link no seu blog
Acho que daqui a uns dias eu escrevo mais sobre reflection, mas vou tentar utilizar uns exemplos mais complexos, se tiverem sugestões para o próximo post, é só deixar nos comentários.
If you enjoyed this post, make sure you subscribe to my RSS feed!Tags: Java, lprodjava, produtividade, reflection
Urubatan,
bastante interessante o post por mostrar uma caracteristica muito poderosa da linguagem. Poderia me enviar alguns links que você tenha como úteis sobre reflection?
Poderia ainda fazer um post de como um
método poderia ser executado quando uma classe possuir uma annotation especifica?
[Translate]
Urubatan,
Cada dia para mim fica comprovado que o tempo realmente melhora as pessoas e não o contrário, vc realmente evoluiu muito, no conceito que eu tinha até hoje ver vc escrevendo um post para ensinar a trilha da qualidade para os programadores que estão vindo seria totalmente impensável.
Nunca esqueço um vez, uns 9 anos atrás quando estávamos naquela correria da topázio e eu estava no primeiro projeto JEE da empresa (vc era a nossa referência em JAVA, já na época) e ouvi de vc assim “Leia primeiro este livro todo (apontando para um livro de J2EE) e depois venha falar comigo”, realmente na época não era o que eu gostaria de ouvir, mas em termos me ajudou.
Enfim, pela sua nova postura, disposto a melhorar e facilitar a vida dos programadores que estão trilhando te respeito muito como profissional.
[Translate]
[...] por estática – Convert Dynamic to Static Construction Algumas vezes a utilização de refletion pode criar fraquezas para o sistema, se isto não for necessário, então prefira inicializar [...]
[Translate]
Urubatan,
Estou começando a aprender reflexão com Java, e achei bastante interessante seu post para começar.
Não entendi direito onde vc comentou
“Via reflexão também é possível por exemplo, executar as seguintes ações em um objeto de uma classe que não existia no momento em que o código foi desenvolvido (um plugin por exemplo):”, vc poderia comentar mais sobre isso por favor.
[Translate]
em Primeiro Lugar, muito bom o post…
se possivel, faz o exemplo com Proxy, de métodos tranzacionais usando @AnotaçãoTranzacion …
abraços Tomaz Lavieri
[Translate]
package test;
class Call {
def clazz = Class.forName(args[0])
println clazz.newInstance().”arg[1]“()
}
Groovy é mais elegante.
[Translate]
sim, Groovy é uma linguagem dinâmica
mas mesmo assim, reflection no groovy é triste, só por que ele roda sobre a JVM. Mas continua sendo um assunto que todo programador Java deve conhecer
[Translate]
Não entendi. A reflection de Groovy é triste por rodar na JVM???
[Translate]
o suporte a reflection da JVM é muito fraco, o groovy faz o que pode
[Translate]
Nunca vi ninguem reclamando da api de reflexao(tb nunca procurei) mas groovy tb usa bastante ASM devem ter um bom motivo.
Mesmo assim, se reflection em groovy é ruim por causa de JVM, isso tb nao afeta o proprio java(e todas a linguagens da jvm, jruby, jython, etc)?
Ou voce quer dizer que reflection api em groovy é pior que em java?
[Translate]
Reflection em groovy não é ruim, ela só é difícil de usar e mais trabalhosa do que o necessário por que o groovy tenta imitar o java …
Outras linguagens da JVM não são afetadas por que não tentam imitar o java, no caso do jruby as classes Ruby nem são compativeis com as classes java
[Translate]
Groovy nao tenta imitar o java, grande parte do groovy é java, inclusive ao usar reflections você precisa importar java.lang.reflect.* e efetivamente usar a api de relections do java.
O groovy só te abstrai a burocracia, e repassa suas chamadas para uma classe java.
Sinceramente não vejo como é mais trabalhosa, me parece muito menos comparando a tutorial de Reflections da sun com esse http://docs.codehaus.org/display/GROOVY/JN3535-Reflection (nunca usei nenhuma a fundo)
[Translate]
Pra quem ainda não conhece, existe o projeto Mirror, que facilita horrores o uso de reflexão.
http://projetos.vidageek.net/mirror-pt/projeto/
Att,
Rodrigo
[Translate]
Bah kra, o blog ta show. O teu tutorial (da parte de executar métodos) humilhou até o do GUJ, parabéns. Fiz os testes e funcionou direitinho. Gostaria que você desse um exemplo de como executar métodos que necessitem de parâmetros. Será que é possível?
aguardo resposta por e-mail
vlw
[Translate]
Urubatan,
Na especialização que estou fazendo em Projetos de Sistemas, o prof solicitou que mostrasse-mos as informações de algumas classes utilizando reflexão ao invés dos métodos get da classe. O teu blog ajudou pacas, valeu!. Nas diversas consultas que fiz na internet sobre o assunto, fiquei sentindo falta de informações de como modificar um atributo privado de uma classe utilizando reflexão, mas não utilizando os métodos da classe para isso. E que tal se vc postasse um caso de uso mais complexo no blog. É muito legal ver os valores dos atributos e coisa e tal… Mas onde a reflexão é realmente utilizada? Daria para criar um cenário e propor a solução com reflexão:
Abraços
Eduardo
[Translate]
Eu uso reflection há algum tempo, mas desde o início até hoje não consegui realizar algo, que é acessar os atributos private ou protected de uma classe. Simplesmente da a excessão de que não posso acessar esses atributos devido ao modificador de acesso e isso está implícito em SecurityManager se não me engano mas não consegui até hoje fazer isso, tipo, quebrar a barreira de private em tempo de execução e pegar o valor do atributo mesmo que ele seja privado se usar um método public get… Você sabe como me ajudar com isso? A propósito gostei muito do seu post!
[Translate]
é só chamar o método setAcessible mas só vai funcionar se não estiver em um ambiente que tenha o SecurityManager ativado
[Translate]
Pois é nem fala, esse é o problema estou em um ambiente em que está ativado o SecurityManager.
Eu não consegui ainda uma forma de mudar isso. Eu não queria desativar o SecurityManager. Será que deve haver outro jeito, já li em alguns lugares que certos frameworks conseguem esse acesso de alguma forma!
[Translate]
[...] http://www.urubatan.com.br/java-tem-espelhos-e-o-mago-deve-saber-jogar-com-eles-basico-da-reflexao/ [...]
[Translate]