Segundo a Wikipedia, Reflection(Reflecção) no contexto da ciência da computação, é a capacidade de um programa de observar ou até mesmo modificar a sua estrutura ou comportamento e que ocorre tipicamente em tempo de execução.

Às vezes nos encontramos em certas situações em que a produção de código pode ir para as alturas. Já em outras precisamos deixar o sistema plugavel a outros componentes, mesmo depois de pronto e implantado. Sabendo disso poderíamos implementar algumas interfaces e preparar um mecanismo de acoplamento do novo componente, mas certamente não irá satisfazer a todos as necessidades da “plugabilidade”. Uma outra usabilidade interessante é a seguinte:

A reflexão pode ser também utilizada para adaptar um determinado sistema dinamicamente à diferentes situações. Considere, por exemplo, uma aplicação que use uma classe X para comunicar-se com algum serviço. Agora, suponha que essa aplicação precise comunicar-se com um serviço diferente, usando a classe Y, que tem nomes de métodos diferentes. Sem reflexão, a aplicação teria de ser modificada e recompilada. Mas, se a reflexão for usada, isso pode ser evitado. A aplicação poderia conhecer os métodos da classe X e essa classe lhe diria que método era usado para que propósito. Assim, quando um novo serviço for usado, via classe Y, a aplicação também procuraria pelos métodos necessários e os utilizaria. Nenhuma modificação no código seria necessária. Nem mesmo o nome da nova classe deveria ser recompilado com a aplicação, uma vez que ele poderia estar armazenado em algum arquivo de configuração, ser verificado e ter a sua classe carregada em tempo de execução. Esse mecanismo é o comumente conhecido como Plugin. (Wikipedia).

Como forma de visualizar e praticar um simples exemplo irei comentar o que ocorreu comigo a algum tempo atrás. Certa ocasião eu me deparei com um problema, que provavelmente muitos de nós já passamos, que consistia em acessar um método de um conjunto de objetos sem saber o tipo concreto do objeto. Esse problema incide diretamente na produtividade de qualquer equipe de desenvolvimento e que sem o uso do reflection, no mínimo, iria escrever algumas dezenas de linhas de código. Para descrever um ambiente no qual este problema está inserido, podemos imaginar que uma equipe esta precisando acessar o método setSelected(boolean) de um conjunto de JcheckBoxs, os quais estão inseridos dentro de um JPanel. Pensem em aproximadamente 100 JCheckBox. Já imaginaram como controlar os métodos clear(), setSelected(boolean), getSelected(), etc, para cada JCheckBox. Imaginaram também a quantidade de código que teríamos que escrever (e a repetição de código). Certamente é um trabalho bem árduo e cansativo.

Para resolver esse pequeno problema (na verdade um incomodo) eu escrevi o seguinte código que está escrito na Listagem 01;


private void ControlSelectedByReflection(JPanel panel){
		
	for (Object obj : panel.getComponents()) {
			
		Class cls = obj.getClass();
		try{
			Method mtd = cls.getMethod("setSelected", new Class[]
      {boolean.class} );
			mtd.invoke(obj, new Object[] {true});
				
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}
Listagem 1

Como podemos verificar a resolução é muito simples, sem contar que conseguimos reduzir com isso, mais ou menos umas 200 linhas de código e mantendo o código limpo e inteligível. Segue abaixo o diagrama de sequência onde podemos perceber o que esta ocorrendo por trás das cortinas.

Figura 1
Figura 1

O que ocorre nesse exemplo é que ao receber o contêiner, neste caso um JPanel, percorremos todos os objetos que estão dentro dele, e para cada objeto realizamos uma introspecção dentro do próprio objeto, e isso em tempo de execução e sem saber qual o tipo concreto. Com o objeto em mãos solicitamos o classe dele com o método obj.getClass(). Com a classe à disposição fazemos uma varredura dentro dentro dela, a procura de um método cujo o nome é setSelected, responsável por selecionar o JCheckBox, e passamos ainda a assinatura do método, a qual é booleana:


cls.getMethod("setSelected", new Class[] {boolean.class} );

Caso não ache, será lançada a exceção NoSuchMethodException.

Encontrando o método, o executaremos dentro do objeto real com o método da seguinte forma:


mtd.invoke(obj, new Object[] {true});

Caso o método invoke não puder chamar o método em questão(setSelected) será lançada a exceção IllegalAccessException.

Para completar a primeira parte deste artigo, segue abaixo um exemplo funcional para problema proposto acima (Listagem 02). Espero que tenham chegado até aqui sem muitos problemas. Sei que com o uso do Reflection a produtividade em qualquer equipe aumenta consideravelmente, sem entra no mérito da manutenibilidade, escalabilidade, entre outros aspectos. Qualquer dúvida, podem entrar em contato comigo. Estarei à disposição para ajudar no que for possível. Saliento que o exemplo a seguir foi feito com o VE (Visual Editor) do Eclipse.

mcjrpart1fig02.jpg
Figura 2. Visual do exemplo funcional

import javax.swing.*;
import java.awt.Rectangle;
import java.lang.reflect.Method;
import javax.swing.border.EtchedBorder;

public class FormReflection extends JFrame {
	private static final long serialVersionUID = 1L;
	private JPanel jContentPane = null;
	private JButton jButton = null;
	private JPanel jPanel = null;
	private JCheckBox jCheckBox = null;
	private JCheckBox jCheckBox1 = null;
	private JCheckBox jCheckBox2 = null;
	private JCheckBox jCheckBox3 = null;
	private JCheckBox jCheckBox4 = null;
	private JCheckBox jCheckBox5 = null;
	private JButton getJButton() {
		if (jButton == null) {
			jButton = new JButton();
			jButton.setBounds(new Rectangle(178, 112, 143, 25));
			jButton.setText("Selecionar Todos");
			jButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					ControlSelectedByReflection(jPanel);
				}
			});
		}
		return jButton;
	}
	private JPanel getJPanel() {
		if (jPanel == null) {
			jPanel = new JPanel();
			jPanel.setLayout(null);
			jPanel.setBounds(new Rectangle(13, 22, 108, 194));
			jPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
			jPanel.add(getJCheckBox(), null);
			jPanel.add(getJCheckBox1(), null);
			jPanel.add(getJCheckBox2(), null);
			jPanel.add(getJCheckBox3(), null);
			jPanel.add(getJCheckBox4(), null);
			jPanel.add(getJCheckBox5(), null);
		}
		return jPanel;
	}
	private JCheckBox getJCheckBox() {
		if (jCheckBox == null) {
			jCheckBox = new JCheckBox();
			jCheckBox.setBounds(new Rectangle(12, 15, 79, 21));
			jCheckBox.setText("Check");
		}
		return jCheckBox;
	}
	private JCheckBox getJCheckBox1() {
		if (jCheckBox1 == null) {
			jCheckBox1 = new JCheckBox();
			jCheckBox1.setBounds(new Rectangle(13, 42, 61, 24));
			jCheckBox1.setText("Check");
		}
		return jCheckBox1;
	}
	private JCheckBox getJCheckBox2() {
		if (jCheckBox2 == null) {
			jCheckBox2 = new JCheckBox();
			jCheckBox2.setBounds(new Rectangle(14, 72, 61, 24));
			jCheckBox2.setText("Check");
		}
		return jCheckBox2;
	}
	private JCheckBox getJCheckBox3() {
		if (jCheckBox3 == null) {
			jCheckBox3 = new JCheckBox();
			jCheckBox3.setBounds(new Rectangle(13, 102, 61, 24));
			jCheckBox3.setText("Check");
		}
		return jCheckBox3;
	}
	private JCheckBox getJCheckBox4() {
		if (jCheckBox4 == null) {
			jCheckBox4 = new JCheckBox();
			jCheckBox4.setBounds(new Rectangle(13, 130, 61, 24));
			jCheckBox4.setText("Check");
		}
		return jCheckBox4;
	}
	private JCheckBox getJCheckBox5() {
		if (jCheckBox5 == null) {
			jCheckBox5 = new JCheckBox();
			jCheckBox5.setBounds(new Rectangle(11, 162, 61, 24));
			jCheckBox5.setText("Check");
		}
		return jCheckBox5;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				FormReflection thisClass = new FormReflection();
				thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				thisClass.setVisible(true);
			}
		});
	}

public FormReflection() {
		super();
		initialize();
	}
	private void initialize() {
		this.setSize(435, 258);
		this.setContentPane(getJContentPane());
		this.setTitle("JFrame");
	}
	private JPanel getJContentPane() {
		if (jContentPane == null) {
			jContentPane = new JPanel();
			jContentPane.setLayout(null);
			jContentPane.add(getJButton(), null);
			jContentPane.add(getJPanel(), null);
		}
		return jContentPane;
	}
	
	private void ControlSelectedByReflection(JPanel panel){
		
		for (Object obj : panel.getComponents()) {
			
			Class cls = obj.getClass();
			try{
				Method mtd = cls.getMethod("setSelected", new Class[] 
        {boolean.class} );
				mtd.invoke(obj, new Object[] {true});
				
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
 }
Listagem 2. Código de uma aplicação funcional com JCheckBox
Referências: