Introduction
Bean Concepts
Properties and Design Patterns
Implicit Introspection
Explicit Introspection
Reflection
A Reflection Example
Serialization
The goal is to be able to write tools that do not have to be recompiled in order to be used. A "beans aware" builder tool maintains Beans in a pallette or toolbox. You can:
The builder tool should not need any predefined knowledge of the Bean. This allows new Beans to be added at any time.
For example, a Java Label has a text field that can be set with setText and gotten with getText. Part of the reason for the many name changes between JDK 1.0 and JDK 1.1 is an attempt the follow this new naming convention. For example, the component method bounds was changed to getBounds. There is also a setBounds method.
As another example, the resize method of a component has been renamed to setSize and there is also a getSize method.
For boolean variables, instead of using a get method, an is method is used. For example, a TextComponent has a property called editable which indicates whether the text can be edited. It is set with setEditable(boolean) and tested with isEditable.
You have probably used setVisible(boolean) for components. There is also a isVisible() which returns a boolean.
If the methods of a property are public, the property is said to be exposed.
It can find out about properties of a bean by either an implicit or explicit method. The implicit method uses design patterns.
The Introspector creates a list of all of the public methods in the bean and searches that list for signatures that match a particular pattern.
Suppose that the following signature is found:
Here are some of the design patterns currently supported:
get/set
These indicate that the Bean has a property propertyName of class PropertyType.
If either of these is detected, the introspector creates a property descriptor for the property called propertyName which is the decapitalized version of PropertyName.
is/get/set
As above for boolean variable if any of these are detected.
Indexed Property Pattern
Public Method Pattern
The introspector creates method descriptors for all of the bean's public
methods including all of the public methods of the bean's superclasses.
Multicast Event Set Pattern
If both signatures are detected, the introspector creates an event set descriptor for an event set called eventName which is the decapitalized version of EventName.
For example, the presence of the following methods indicates that the bean fires action event sets that will be processed by ActionListeners:
public void addActionListener(ActionListener listener) public void removeActionListener(ActionListener listener)
The Introspector first appends BeanInfo to the package-qualified class name of the bean and then tries to instantiate a class with that name. If this does not work it tries the search path.
If successful it eventually finds a BeanInfo for the Bean. The getBeanInfo method takes a Bean class object as a parameter and returns a BeanInfo object.
The BeanInfo class contains a number of get methods for obtaining information about a bean, such as getEventSetDescriptors which contains information about the kinds of events fired by this bean.
The getPropertyDescriptors method returns an array of PropertyDescriptor objects, each of which can be used to get information on a property of the Bean.
There are two types of applications that are likely to use reflection.
More explicitly, suppose you had a variable of type MyClass and a String,
MyMethod, which is the name of a method in the class MyClass.
How would you execute that method?
Reflection allows you to create a reflected class from a given instance of a class. The reflected class allows you to:
Among the methods in the class Method are:
For example, the following method is part of the Array class:
public static Object newInstance(Class componentType, int length)
creates a new array, like the creation expression:
import java.lang.reflect.*; import java.io.*; import java.awt.*; public class ClassContents { public char thisIsAPublicCharaceter; private char thisIsAPrivateCharaceter; protected char thisIsAPrttectedCharaceter; public Point[] myPoints; private int count = 0; public static void main(String[] args) { try { Class c= Class.forName(args[0]); System.out.println(c); System.out.println("Fields follow:"); printMembers(c.getFields()); System.out.println("Declared Fields follow:"); printMembers(c.getDeclaredFields()); System.out.println("Constructor follow:"); printMembers(c.getConstructors()); System.out.println("Methods follow:"); printMembers(c.getMethods()); } catch (ClassNotFoundException e) { System.out.println("unknown class: " + args[0]); } getStringAndMethod(); } private static void printMembers(Member[] mems) { for (int i=0;i < mems.length;i++) { if (mems[i].getDeclaringClass() == Object.class) continue; String decl = mems[i].toString(); System.out.print(" "); System.out.println(decl); } } public static Point metodThatReturnsAPoint(int x) { return new Point(x,x); } private static void getStringAndMethod() { ... } private static String getInput() { ... } }This produces the following output:
java ClassContents ClassContents class ClassContents Fields follow: public char ClassContents.thisIsAPublicCharaceter public java.awt.Point[] ClassContents.myPoints Declared Fields follow: public char ClassContents.thisIsAPublicCharaceter private char ClassContents.thisIsAPrivateCharaceter protected char ClassContents.thisIsAPrttectedCharaceter public java.awt.Point[] ClassContents.myPoints private int ClassContents.count static java.lang.Class ClassContents.class$java$lang$Object static java.lang.Class ClassContents.class$java$lang$String Constructor follow: public ClassContents() Methods follow: public static void ClassContents.main(java.lang.String[]) public static java.awt.Point ClassContents.metodThatReturnsAPoint(int)Notes:
private static void getStringAndMethod() { byte[] myInput; myInput = new byte[80]; String str; String parameter; Object[] parameterArray; String myMethodString; Class StrClass; Method myMethod; Object result; System.out.println("This will execute one String method on a String"); System.out.println("The String method must take a single String parameter"); System.out.println("Some possibilities are: concat, endsWith, equals, indexOf, lastIndexOf"); try { System.out.println("Enter a string"); str = getInput(); System.out.println("String is "+str+" with length "+str.length()); System.out.println("Enter a String method which takes a single String parameter:"); myMethodString = getInput(); System.out.println("Enter the parameter to be used with "+myMethodString); parameter = getInput(); parameterArray = new Object[1]; parameterArray[0] = parameter; System.out.println("about to invoke (\""+str+"\")."+myMethodString+"(\""+parameter+"\")"); StrClass = str.getClass(); if (StrClass == null) { System.out.println("Could not get class from string"); return; } myMethod = StrClass.getMethod(myMethodString,new Class[] {String.class}); System.out.println("Got the method"); result = myMethod.invoke(str,parameterArray); System.out.println("The result was "+result); } catch (Exception e) { System.out.println("Exception somewhere"); } } private static String getInput() { byte[] myInput; String str; myInput = new byte[80]; try { System.in.read(myInput); str = new String(myInput); if (str.indexOf("\r") != -1) str = str.substring(0,str.indexOf("\r")); else str = str.substring(0,str.indexOf("\n")); } catch (IOException e) { str = ""; } return str; } }Notes
This will execute one String method on a String The String method must take a single String parameter Some possibilities are: concat, endsWith, equals, indexOf, lastIndexOf Enter a string This is a test String is This is a test with length 14 Enter a String method which takes a single String parameter: indexOf Enter the parameter to be used with indexOf te about to invoke ("This is a test").indexOf("te") Got the method The result was 10
It supports the complementary reconstruction of the object from the stream.
This is useful for
FileOutputStream f = new FileOutputStream("tmp"); ObjectOutputStream s = new ObjectOutputStream(f); s.writeObject("Today"); s.writeObject(new Date()); s.flush();Both String and Date implement Serializable.
To read these back you could use:
FileInputStream in = new FileInputStream("tmp"); ObjectInputStream s = new ObjectInputStream(in); String today = (String)s.readObject(); Date date = (Date)s.readObject();Object serialization is automatic for classes that implement the Serializable interface. To make an object Serializable, you need only implement Serializable, and do not need to implement any additional methods as long as all of the components are primitive or implement Serializable. Values of all fields are saved unless they are declared transient.
If you want to have control over how the object is converted to a stream of bytes, you can implement the Externalizable interface. This requires that you write the following two methods:
public void writeExternal(ObjectOutput out) throws IOException public void readExternal(ObjectInput in) throws IOException, ClassNotFoundExceptionOne of the problems with doing this yourself is versioning. The Serializable interface takes care of problems with storing under a different version of Java than the reading.