Matéria de Capa
 

 

 

A construção de componentes utilizando a API Reflection pode levar a um reaproveitamento maior de código, porém a existência de algumas exceções pode tornar sua utilização inviável. Por outro lado, as Annotations permitem a inserção de meta-informações em atributos, métodos e classes, permitindo um tratamento diferenciado, de forma que dentro da rotina que utiliza Reflection as exceções possam ser tratadas da forma correta. Neste artigo, será mostrado como combinar essas duas tecnologias para criar componentes mais flexíveis e aumentar a produtividade da equipe.

A API Reflection permite que seja possível obter, em tempo de execução, informações sobre classes, métodos e atributos. Com essa informação, é possível criar uma instância de uma determinada classe que não se conhece em tempo de compilação, ou mesmo executar um método dessa classe possuindo somente seu nome e os tipos de seus parâmetros. Essas funcionalidades são utilizadas em diversos frameworks conhecidos para eliminar trabalhos considerados braçais. Essa API é utilizada, por exemplo, pelo WebWork e o Struts para colocar os parâmetros recebidos pelo request dentro das propriedades de um Java bean e pelo Hibernate para popular os objetos com informações de uma base de dados. Um grande amigo meu dizia que conhecer e utilizar a API Reflection é o que separa “os homens dos meninos”.

Os frameworks citados como exemplo no parágrafo anterior se baseiam em arquivos XML auxiliares para saberem quais classes devem ser instanciadas e quais métodos devem ser chamados. Esses arquivos XML possuem meta-informações a respeito das classes, dizendo, por exemplo, se uma classe é persistente ou qual classe deve atender a uma determinada requisição. Até hoje ainda não encontrei alguém que fosse fã de um descritor XML, no máximo costuma-se considerá-lo um mal necessário. Para facilitar a vida do desenvolvedor no gerenciamento dessas meta-informações, foram criadas então as Annotations.

O objetivo deste artigo não é se aprofundar nos fundamentos de Reflection e Annotations, mas mostrar como se pode tirar proveito desses recursos para resolver problemas do dia-a-dia. Inicialmente, será dada uma rápida introdução sobre essas duas tecnologias, porém tudo será mostrado de uma forma rápida e resumida, focando na base necessária para o entendimento dos exemplos. O restante do artigo mostrará situações em que o uso das Annotations juntamente com a API Reflection resulta em soluções flexíveis e reutilizáveis. Cada exemplo será baseado em problemas práticos (que pessoalmente já precisei enfrentar), e durante a explicação de cada um serão abstraídas situações mais genéricas em que soluções similares poderiam ser utilizadas. O código-fonte completo dos exemplos está disponível para download no site da revista.

A API Reflection

Como já foi dito, não é o objetivo deste artigo descrever por completo a API Reflection, porém esta sessão fará uma pequena introdução e mostrará os principais conceitos utilizados nos exemplos. Para quem quiser uma introdução mais detalhada sobre Reflection, eu indico o artigo “O pacote java.lang.reflect” publicado na MundoJava nº 8. A API Reflection nos permite obter, em tempo de execução, meta-informações a respeito de classes, como, por exemplo, quais interfaces implementa, quais são seus métodos e quais são seus atributos. Utilizando essas informações, é possível desenvolver um componente que, por exemplo, executa um método sem o conhecer em tempo de compilação.

Muitas pessoas acreditam que utilizar as funcionalidades da API Reflection é somente para aqueles gurus que começaram a programar com 6 anos e são iniciados em alguma irmandade secreta. Na verdade, não existe muito segredo na utilização de Reflection, inclusive as classes dessa API representam entidades de um domínio conhecido por qualquer programador: classes, métodos, atributos, etc. Na tabela 1, estão apresentados exemplos de código que mostram como executar algumas funcionalidades-chave da API Reflection. O objetivo dessa tabela é cobrir o necessário para a compreensão dos exemplos deste artigo e servir como uma referência rápida para os desenvolvedores.


Tabela 1. Tabela com algumas funcionalidades da API Reflection.

Os métodos mostrados na tabela 1 para a recuperação de métodos e atributos funcionam apenas para membros públicos da classe, o que inclui o que foi herdado das superclasses. Para recuperar membros com outras visibilidades, como private, protected e default, deve-se utilizar os métodos que possuem “declared”, como getDeclaredMethods() ou getDeclaredField(). Esses métodos retornam membros declarados na classe, independentemente de sua visibilidade. Dessa forma, um método público da superclasse não pode ser recuperado pelo método getDeclaredMethod(), a não ser que esse método seja sobreposto na subclasse. Com isso, caso seja necessário recuperar todos os atributos de uma classe, não importando sua visibilidade e onde foram declarados, deve-se fazer um loop percorrendo todas as superclasses (que pode ser recuperada com o método getSuperclass()) e chamando o método getDeclaredFields() em cada uma delas. A seguir segue um pequeno trecho de código que imprime todos os atributos de uma classe incluindo os herdados das superclasses, independentemente do nível de acesso.

Class classeBase = Class.forName(“org.mundojava.Classe”);
for(Class classe = classeBase; classe != null; classe = classe.getSuperclass())
for(Field f : classe.getDeclaredFields())
System.out.println(f.getName());

Algumas novidades do J2SE 5.0, como o autoboxing/unboxing e o varargs, facilitaram bastante a utilização da API Reflection. Para a invocação de um método, é necessário passar como parâmetro um array de objetos, contendo os parâmetros que serão passados para o método. Quando um dos parâmetros era de um tipo primitivo (exemplo, int), era preciso utilizar a classe wrapper para passar o parâmetro (exemplo, Integer). Com os recursos de autoboxing e unboxing o tipo primitivo pode ser passado diretamente como parâmetro sem a necessidade do uso do wrapper. O método invoke() da classe Method, para simplificar, agora suporta o uso de varargs, o que permite passar os parâmetros diretamente sem a necessidade da criação do array. Segue um exemplo ilustrando como a utilização da API foi simplificada com utilização desses novos recursos. Pode paracer estranho na primeira linha serem passados dois parâmetros e na segunda linha serem passados três, porém é isso mesmo, pois o recurso de varargs faz com que cada item do array possa ser incorporado como um parâmetro no método invoke().

J2SE 1.4: metodo.invoke(objeto,
new Object [] {“Parametro”,new Integer(10)});
J2SE 5.0: metodo.invoke(objeto, “Parametro”, 10);

A utilização de Reflection em uma aplicação tem duas principais desvantagens: a perda em desempenho e a maior possibilidade de erros. Invocar métodos e recuperar atributos utilizando Reflection é muito mais custoso em termos de desempenho do que se o mesmo fosse feito da forma normal. Como foi dito no artigo “Otimização de Performance para Aplicações Distribuídas” na MundoJava nº 17, quando estamos otimizando uma funcionalidade, o gargalo deve ser detectado e o esforço deve ser priorizado em cima dele. Felizmente, na maioria dos casos o gargalo não se encontra na utilização de Reflection, porém vale a pena tomar cuidado para não abusar e não usar dentro de loops muito grandes, etc. Normalmente, os ganhos em reusabilidade e flexibilidade compensam bastante as pequenas perdas em desempenho. Se o Hibernate e o Struts utilizam Reflection e são utilizados em aplicações eficientes e robustas, por que o seu componente não pode usar?

Vários erros que são pegos em tempo de compilação só aparecem em tempo de execução com a utilização de Reflection. Como exemplo pode ser citada a recuperação de um método que não existe ou a invocação de um método com parâmetros de tipos errados. Isso é um problema, pois a aplicação fica suscetível a um maior número de erros, o que, se não for devidamente tratado, pode causar impactos na robustez. Felizmente, com um bom mecanismo de tratamento de erro e a utilização de testes unitários, é possível criar componentes robustos e confiáveis com a utilização de Reflection.

Apesar da API Reflection possibilitar a criação de componentes mais flexíveis, ela não pode ser considerada a cura para todos os males. Em muitos casos, as técnicas de orientação a objetos e os padrões de projeto são a melhor opção para dar a flexibilidade adequada a uma aplicação. A criação de instâncias utilizando Reflection, por exemplo, deve ser feita quando o componente só tem o conhecimento da classe que deve ser criada em tempo de execução. Em outros casos, padrões de projeto como Abstract Factory ou Factory Method podem ser utilizados de forma mais adequada. O polimorfismo é uma característica das linguagens orientadas a objetos que permite que, a partir de uma interface ou uma superclasse em comum, objetos diferentes possam ser tratados da mesma forma, mesmo que possuam comportamentos diferentes. A API Reflection não deve ser utilizada para esses casos e sim em rotinas em que não faça sentido os objetos compartilhem a mesma interface, como em Java beans, por exemplo. Deve-se tomar muito cuidado, pois o uso inadequado de Reflection pode adicionar complexidade desnecessária e causar perdas de performance, sem se ter nenhum ganho real com a sua utilização.

Uma funcionalidade avançada da API Reflection, adicionada a partir da J2SE 1.4, são os proxys dinâmicos. Proxy é um padrão de projeto em que uma classe implementa a mesma interface de uma outra e assume seu lugar, delegando a chamada de métodos para a classe original quando necessário. Um proxy dinâmico consegue interceptar os métodos sem precisar implementar a mesma interface da classe original, assumindo dinamicamente esse comportamento. Dessa forma, a mesma implementação de um proxy dinâmico pode ser utilizada em diversas interfaces. Os proxys dinâmicos serão utilizados no último exemplo deste artigo e serão explicados com mais detalhes durante o mesmo.

Leia o artigo completo na revista MUNDOJAVA, já nas bancas!


 




 
© 2003 - MundoJava - Todos os direitos reservados <design: www.id-art.com.br >