Basics

Suppose you are walking down the street and a stranger approaches and hands you an object. What would you do? Naturally, you would look at whatever was handed to you. You inspect the item, and in an instant determine that the object is a piece of paper. Instantly, you know many things about this object: one can write on it; it can have messages such as advertisements, warnings, news, and other useful information printed on it; and you can even crush it into a ball and play basketball when your boss isn't looking. Though this is a silly example, it does illustrate the basic idea of reflection, which allows you to conduct a similar inspection of Java objects and classes. Using a combination of reflection and introspection, you can determine the nature and possible function of an object that you didn't know about at compile time. Furthermore, you can use this information to execute methods or set field values on the object. To see how reflection works, let's start with a class from your Online Only Bank data model. In Example 9-1, the Person class from the data model is displayed without any comments or content from its methods.

Example 9-1. A Person in a bank data model
package oracle.hcj.bankdata;
public class Person extends MutableObject {
 public static final ObjectConstraint GENDER_CONSTRAINT =
 new ObjectConstraint("gender", false, Gender.class);
 public static final StringConstraint FIRST_NAME_CONSTRAINT =
 new StringConstraint("firstName", false, 20);
 public static final StringConstraint LAST_NAME_CONSTRAINT =
 new StringConstraint("lastName", false, 40);
 public static final DateConstraint BIRTH_DATE_CONSTRAINT =
 new DateConstraint("birthDate", false, "01/01/1900", "12/31/3000", Locale.US);
 public static final StringConstraint TAX_ID_CONSTRAINT =
 new StringConstraint("taxID", false, 40);
 private Date birthDate = Calendar.getInstance( )
 .getTime( );
 private Gender gender = Gender.MALE;
 /** The first name of the person. */
 private String firstName = "<<NEW PERSON>>";
 private String lastName = "<<NEW PERSON>>";
 private String taxID = new String( );
 public void setBirthDate(final Date birthDate) { }
 public Date getBirthDate( ) { }
 public void setFirstName(final String firstName) { }
 public String getFirstName( ) { }
 public void setGender(final Gender gender) { }
 public Gender getGender( ) { }
 public void setLastName(final String lastName) { }
 public String getLastName( ) { }
 public void setTaxID(final String taxID) { }
 public String getTaxID( ) { }
 public boolean equals(final Object obj) { }
 public int hashCode( ) { }
}


All of your Online Only Bank data model classes roughly look like this. They are all JavaBeans—that is, they conform to the naming standard that is set for JavaBeans. Since all constructed classes in Java ultimately inherit from java.lang.Object (see ), you can pass an instance of this class as an object to a method and then figure out which properties, methods, inner interfaces, etc., the class contains, as Example 9-2 demonstrates.

Example 9-2. Getting method information on an object
package oracle.hcj.reflection;
import java.lang.reflect.Method;
import oracle.hcj.bankdata.Person;
import oracle.hcj.bankdata.Gender;
public class MethodInfoDemo {
 public static void printMethodInfo(final Object obj) {
 Class type = obj.getClass( );
 final Method[] methods = type.getMethods( );
 for (int idx = 0; idx < methods.length; idx++) {
 System.out.println(methods[idx]);
 } }
 /** * Demo method.
 *
 * @param args Command line arguments.
 */
 public static void main(final String[] args) {
 Person p = new Person( );
 p.setFirstName("Robert");
 p.setLastName("Simmons");
 p.setGender(Gender.MALE);
 p.setTaxID("123abc456");
 printMethodInfo(p);
 }
}


Inside printMethodInfo( ), inspect the object passed to you. After you determine the class of the object, examine contents of the class and print the string version of the methods:

>ant -Dexample=oracle.hcj.reflection.MethodInfoDemo run_example run_example:
 [java] public int oracle.hcj.bankdata.Person.hashCode( )
 [java] public boolean oracle.hcj.bankdata.Person.equals(java.lang.Object)
  [java] public void oracle.hcj.bankdata.Person.setFirstName(java.lang.String)
 [java] public void oracle.hcj.bankdata.Person.setLastName(java.lang.String)
 [java] public void oracle.hcj.bankdata.Person.setGender (oracle.hcj.bankdata.Gender)
 [java] public void oracle.hcj.bankdata.Person.setTaxID(java.lang.String)
 [java] public void oracle.hcj.bankdata.Person.setBirthDate(java.util.Date)
 [java] public java.util.Date oracle.hcj.bankdata.Person.getBirthDate( )
 [java] public java.lang.String oracle.hcj.bankdata.Person.getFirstName( )
 [java] public oracle.hcj.bankdata.Gender oracle.hcj.bankdata.Person.getGender( )
 [java] public java.lang.String oracle.hcj.bankdata.Person.getLastName( )
 [java] public java.lang.String oracle.hcj.bankdata.Person.getTaxID( )
 [java] public java.lang.String oracle.hcj.datamodeling.MutableObject.toString( )
 [java] public void oracle.hcj.datamodeling.MutableObject.
 addPropertyChangeListener
 (java.lang.String,java.beans.PropertyChangeListener)
 [java] public void oracle.hcj.datamodeling.MutableObject.
 addPropertyChangeListener(java.beans.PropertyChangeListener)
 [java] public void oracle.hcj.datamodeling.MutableObject.
 removePropertyChangeListener
 (java.lang.String,java.beans.PropertyChangeListener)
 [java] public void oracle.hcj.datamodeling.MutableObject.
 removePropertyChangeListener(java.beans.PropertyChangeListener)
 [java] public final native java.lang.Class java.lang.Object.getClass( )
 [java] public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
 [java] public final void java.lang.Object.wait( ) throws java.lang.InterruptedException
 [java] public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
 [java] public final native void java.lang.Object.notify( )
 [java] public final native void java.lang.Object.notifyAll( )


The getMethods( ) call in printMethodInfo( ) returned an array of Method objects from the java.lang.reflect package; the string version of these Method objects was then written to the console. The getMethods( ) call extracted each of the public members of the Person class, including the members it inherited from other classes such as MutableObject. The emphasized lines contain the setters and getters for the Person class. The nice thing about printMethodInfo( ) is that it will work for any object you pass to it.

Screenshot

If you want to get only the methods of the class without the inherited methods, you can use the getDeclaredMethods( ) method defined on Class. However, getDeclaredMethods( ) will also return private and protected members of the class.


In addition to printing out the Method objects, you can use them to execute the methods. This is demonstrated in Example 9-3 with a method that will set all string properties in a class as empty strings.

Example 9-3. Dynamically invoking methods
package oracle.hcj.reflection;
public class MethodInfoDemo {
 public static void emptyStrings(final Object obj)
 throws IllegalAccessException, InvocationTargetException {
 final String PREFIX = "set";  Method[] methods = obj.getClass( )
 .getMethods( );
 for (int idx = 0; idx < methods.length; idx++) {
 if (methods[idx].getName( )
 .startsWith(PREFIX)) {
 if (methods[idx].getParameterTypes( )[0] == String.class) {
 methods[idx].invoke(obj, new Object[] { new String( ) });
 }
 }
 }
 }
}


This method looks for all of the methods in a class that start with the word "set" and take a single String as a parameter. It then invokes these methods by passing an array of objects filled with only a single empty string to the method. The nice thing about the last two methods in the example is that they will work for any object passed to them, not just the Person object. If you have complex code in this demonstration class, you could apply it to a wide variety of objects, just like a carpenter applies a saw to many kinds of wood.

Screenshot

Many pieces of professional software expand on this ability to implement exceedingly complex functionality. For example, the Eclipse IDE is based on a plug-in architecture and uses reflection to invoke plug-ins that the programmers of Eclipse have never seen.


      
Comments