|
Comp202: Principles of Object-Oriented Programming II
|
In the MVC design of the Koch Curve application, since the view cannot directly access any class in the model, it must send a "message" to the model asking for a class to be loaded when the user wants to change what Koch factory is being used. For simplicity's sake, let assume that a drop-list exists in the view that holds the fully qualified class name of the desired factory (i.e. includes the package). When the user selects a factory from the drop-list, the string which holds the class name is sent to the model.
In the model, the appropriate factory class corresponding to the string must be loaded and the appropriate constructor called to instantiate a concrete factory . It is thus necessary to find out how many input parameters are required for the constructor. All of these problems can be resolved by using Java's dynamic class loading capability plus its "reflection" capability. "Reflection" (also called "introspection") is the ability to inspect the defining structure of a class at run time. Let's us take a look at a few key concepts in Java reflection needed to solve our problem.
The starting point of most reflection concerns is a class named Class. In most cases, we will need to import a package named java.lang.reflect to make use of the classes pertaining to reflection.
- Suppose s is a String that stands for the fully qualified name of some class (or interface, or primitive type). For example, s is "kochModel.factory.KochCurveFactory". Then Class.forName(s) will return the unique Class object associated with the class whose name is s. Its signature is
Class<?> forName(String s).
- If we know that a given class has a default constructor, i.e. a constructor with no parameter, then we can create an instance of this class via the method Class.newInstance(). For example,
Class.forName("java.lang.String").newInstance()
will return the empty String: ""
- If we do not have any a priori knowledge on the constructors of a given class, we can query the class for an array of its Constructor objects. Suppose someClass is an object of type Class:
Class someClass;
Then there is a unique class X that is associated with someClass . someClass.getConstructors() will return an array of Constructor objects that correspond to all the public constructors for X. Its signature is
Constructor[] getConstructor().
- We can use any of these Constructor objects to instantiate an object of type X (without knowing what X is) by invoking its newInstance(...) method. newInstance(...) requires an array of Object parameters corresponding to the list of parameters of the associated constructor. Its signature is
T newInstance(Object... args).
- The Constructor class has a method called getParameterTypes() that returns the array of Class objects corresponding to the parameter types of the associated constructor. Its signature is
Class<?>[] getParameterTypes().
Below is a method that makes use of the above reflection capability to change the current factory being used, given a String containing the fully qualified name of the requested class:
// Assume the existence of "aFactory", the current factory in use. public void changeFactoryTo(String s) { try { // Get the first constructor of the class whose fully qualified name is "s": java.lang.reflect.Constructor c = Class.forName(s).getConstructors()[0]; // There are two kinds of factory: basic and composite. // The constructor for the basic factory needs no parameter. // The constructor for the composite factory needs another factory as the parameter. // Get an array of arrays of input parameters. // Here, arrays for zero and one input parameter: Object[][] args = new Object[][]{new Object[]{}, new Object[]{aFactory}}; // Create a new instance given the number of input parameters for constructor: aFactory = (AFactory)c.newInstance(args[c.getParameterTypes().length]); } catch(Exception ex) { System.out.println(ex); } }
Last Revised Thursday, 03-Jun-2010 09:52:32 CDT
©2007 Stephen Wong and Dung Nguyen