Example 12.2 demonstrates another use of the Reflection API. This UniversalActionListener object uses reflection to invoke a named method of a specified object, passing another optionally specified object as an argument. It does this in the framework of the ActionListener interface, so that it can serve as an action listener within a Java 1.1 GUI. By using the reflection capabilities of the UniversalActionListener, a program can avoid having to create a lot of trivial ActionListener implementations to handle action events. The main() method at the end of this example is a simple test GUI that demonstrates how you could use the UniversalActionListener object. Contrast it with the anonymous class event-handling techniques demonstrated in Chapter 7, Events.
Java does not allow methods to be passed directly as data values, but the Reflection API makes it possible for methods passed by name to be invoked indirectly. Note that this technique is not particularly efficient. For asynchronous event handling in a GUI, though, it is certainly efficient enough: indirect method invocation through the Reflection API will always be much faster than the response time required by the limits of human perception. Invoking a method by name is not an appropriate technique, however, when repetitive, synchronous calls are required. Thus, you should not use this technique for passing a comparison method to a sorting routine or passing a filename filter to a directory listing method. In cases like these, you should use the standard technique of implementing a class that contains the desired method and passing an instance of the class to the appropriate routine.
import java.awt.event.*; import java.lang.reflect.*; import java.awt.*; // Only used for the test program below. public class UniversalActionListener implements ActionListener { protected Object target; protected Object arg; protected Method m; public UniversalActionListener(Object target, String methodname, Object arg) throws NoSuchMethodException, SecurityException { this.target = target; // Save the target object. this.arg = arg; // And method argument. // Now look up and save the Method to invoke on that target object. Class c, parameters[]; c = target.getClass(); // The Class object. if (arg == null) parameters = new Class[0]; // Method parameter. else parameters = new Class[] { arg.getClass() }; m = c.getMethod(methodname, parameters); // Find matching method. } public void actionPerformed(ActionEvent event) { Object[] arguments; if (arg == null) arguments = new Object[0]; // Set up arguments. else arguments = new Object[] { arg }; try { m.invoke(target, arguments); } // And invoke the method. catch (IllegalAccessException e) { // Should never happen. System.err.println("UniversalActionListener: " + e); } catch (InvocationTargetException e) { // Should never happen. System.err.println("UniversalActionListener: " + e); } } // A simple test program for the UniversalActionListener. public static void main(String[] args) throws NoSuchMethodException { Frame f = new Frame("UniversalActionListener Test");// Create window. f.setLayout(new FlowLayout()); // Set layout manager. Button b1 = new Button("tick"); // Create buttons. Button b2 = new Button("tock"); Button b3 = new Button("Close Window"); f.add(b1); f.add(b2); f.add(b3); // Add them to window. // Specify what the buttons do. Invoke a named method with // the UniversalActionListener object. b1.addActionListener(new UniversalActionListener(b1, "setLabel", "tock")); b1.addActionListener(new UniversalActionListener(b2, "setLabel", "tick")); b1.addActionListener(new UniversalActionListener(b3, "hide", null)); b2.addActionListener(new UniversalActionListener(b1, "setLabel", "tick")); b2.addActionListener(new UniversalActionListener(b2, "setLabel", "tock")); b2.addActionListener(new UniversalActionListener(b3, "show", null)); b3.addActionListener(new UniversalActionListener(f, "dispose", null)); f.pack(); // Set window size. f.show(); // And pop it up. } }