CS 4773 Object Oriented Systems
Java Beans


Start of Class Monday


This is meant to be an introduction to the concepts related to Java Beans.
Much of this comes from the Java Beans Tutorial, the JDK 1.1.5 documentation, and a set of tutorials by Dick Baldwin.
Previous Topic: Monopoly Design

Introduction
Bean Concepts
Properties and Design Patterns
Implicit Introspection
Explicit Introspection
Reflection
A Reflection Example
Serialization

Next Topic: ?



Introduction

Java applets are not easily integrated into other applications. A Java Bean is a Java class that follows a specific set of interface specifications. The intention is to produce reusable software components that can be manipulated in an application builder tool.

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.


Bean Concepts


Properties and Design Patterns

An example of a property is an instance variable whose value can be manipulated with set and get methods.

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.


Implicit Introspection

The Java Bean APIs contain a java.beans.Introspector class which can be used to find out about the properties, events, and methods of a class.

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:

public void setFlavor(String f)
It will assume that the bean has a property called "flavor" and that this method is the write accessor for the property.

Here are some of the design patterns currently supported:

get/set

public PropertyType getPropertyName()
public void setPropertyName(PropertyType v)

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

public boolean isPropertyName()
public boolean getPropertyName()
public void setPropertyName(boolean v)

As above for boolean variable if any of these are detected.

Indexed Property Pattern

public PropertyType[] getPropertyName()
public PropertyType getPropertyName(int i)
public void setPropertyName(PropertyType[] v)
public void setPropertyName(int i, PropertyType v)

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

public void addEventNameListener(EventNameListener l)
public void removeEventNameListener(EventNameListener l)

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)

Explicit Introspection

This uses an array of package names to form a search path for locating explicit information about a Bean.

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.


Reflection

Reflection enables Java code to discover information about the fields, methods, and constructors of loaded classes and to use reflected fields, methods, and constructors to operate on their underlying counterparts.

There are two types of applications that are likely to use reflection.

Example: Suppose you load a class and can find out the name (and parameter types) of a method of that class.
How would you execute the method?

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:

One of the new classes in the Core Reflection API is the Method class. An object of the Method class provides information about and access to a single method of a class or interface.

Among the methods in the class Method are:

In addition, the Array class contains static methods that allow you to create arrays of objects of a given type.

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:

new componentType[length]


A Reflection Example

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: Here is the code for the rest of it:
   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 Result of a sample run
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

Serialization

Object serialization supports the encoding of objects and the objects reachable from them into a stream of bytes.

It supports the complementary reconstruction of the object from the stream.

This is useful for

Objects that implement the Serializable or Externalizable interface can be written and read as in the following example:
   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, ClassNotFoundException
One 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.



Next topic: ?