Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Java Alley | JavaBeans from Square One (Web Techniques, Sep 1997)


Java Alley | JavaBeans from Square One (Web Techniques, Sep 1997)

JavaBeans from Square One

By Bruce Eckel

Java is invaluable for creating reusable pieces of code, and the most reusable unit of code is the class, which comprises a cohesive unit of characteristics and behaviors that can be reused, either via composition or through inheritance.

Inheritance and polymorphism are essential to object-oriented programming, but when you're putting together an application, what you really want is components that you can drop into your design like an electronic engineer places chips on a circuit board (or even, in the case of Java, onto a Web page). It seems, too, that there should be some way to accelerate this module-assembly style of programming.

Visual programming first became successful, very successful, with Microsoft's Visual Basic (VB), followed by Borland's Delphi (the primary inspiration for the JavaBeans design). The reason that visual programming tools have been so successful is that they dramatically speed up the process of building an application -- certainly the user interface -- but often other portions of the application as well. These programming tools represent components visually, which makes sense since they usually display some kind of visual component, like a button or text field. The visual representation is, in fact, often identical to the component's appearance in the running program. So part of visual programming involves dragging a component from a palette and dropping it onto a form -- the application-builder tool writes code as you do this -- and that code causes the component to be created in the running program.

Simply dropping the component onto a form is normally not enough to complete the program. Usually the component's characteristics need to be changed: color, font, what database it's connected to, and so on. Modifiable characteristics, or "properties," can be manipulated inside the application-builder tool, and when you create the program this configuration data is saved so that it can be rejuvenated when the program is started.

But an object is more than characteristics; it's also a set of behaviors. At design time, visual-component behaviors are represented by "events," or things that can happen to the component. Normally, you determine what will happen when an event occurs by tying code to it.

Here's the critical part: The application-builder tool can dynamically interrogate a component to find out what properties and events it supports. Once it knows what they are, it can display them, allowing you to change properties and saving the state when you build the program. Generally, double clicking on an event causes the application-builder tool to create a code body and ties it to that particular event. You simply write the code that executes when the event occurs.

Because the application-builder tool manages all these connection details, you can focus on the program's appearance and functionality.

What is a Bean?

After the dust settles, then, a component is really just a block of code, typically embodied in a class. The key issue is the ability of the application-builder tool to discover the properties and events for that component. To create a VB component, the programmer had to write a fairly complicated piece of code, following certain conventions to expose the properties and events. Delphi was a second-generation visual-programming tool actively designed around visual programming, making it much easier to create a visual component. JavaBeans, however, has brought the creation of visual components to its most advanced state, because a Bean is just a class. You needn't write any extra code or use special language extensions to make something a Bean. You simply modify your method name to indicate to the application-builder tool whether it is a property, an event, or just an ordinary method.

For a property named Xxx, for example, you typically create two methods: getxxx and setxxx. (The first letter is automatically decapitalized to produce the property name.) The type produced by the get function is the same as the type of the argument to the set function. The name of the property and the type for the get and set are not related.

You can use this approach for a Boolean property as well, but you may also use isxxx instead of getxxx. Ordinary Bean methods don't conform to this naming convention, but they're public.

Events use the "listener" approach: addFooBarListener(FooBarListener) and removeFooBarListener(FooBarListener) to handle a FooBarEvent. The built-in events and listeners will usually satisfy your needs, but you can also create your own.

(Most of the method-name changes that occurred between Java 1.0 and Java 1.1 had to do with adapting to the get and set naming convention, in order to make that particular component into a Bean.)

We can use these guidelines to create a very simple Bean; see Listing One. You can see that it's just a class. Normally, all your fields will be private, and accessible only through methods. Following the naming convention, the properties are jumps, color, spots, and jumper. Although the name of the internal identifier is the same as the name of the property in the first three cases, jumper shows that the property name does not force you to use any particular name for internal variables (or indeed, to even have any internal variables for that property).

This Bean handles ActionEvent and KeyEvent, based on the naming of the add and remove methods for the associated listener. Finally, the ordinary method croak() is still part of the Bean simply because it's a public method, not because it conforms to any naming scheme.

One of the most critical parts of the Bean scheme occurs when you drag a Bean off a palette and plop it down on a form. The application-builder tool must be able to create the Bean (which it can, if there's a default constructor) and then, without access to the Bean's source code, extract all the necessary information to create the property sheet and event handlers.

Part of the solution is already evident from an earlier column in this series ("Discovering Information at Run Time: Part Deux," March, 1997): Java 1.1 "reflection" allows all the methods of an anonymous class to be discovered. This solves the Bean problem without any extra language keywords like those required in other visual-programming languages. In fact, one of the prime reasons reflection was added to Java 1.1 was to support Beans (as well as object serialization and remote method invocation).

This might lead you to expect that the creator of the application-builder tool would have to reflect each Bean and hunt through its methods to find its properties and events. However, Java designers wanted to provide a standard interface for everyone to use, not only to make Beans simpler to use, but also to provide a standard gateway to the creation of more complex Beans. This interface is the Introspector class, and its most important method is the static getBeanInfo(). When you pass this method a Class handle, it fully interrogates that class and returns a BeanInfo object that can be dissected to find properties, methods, and events.

Normally, you won't care about any of this -- you'll probably get most of your Beans off the shelf, drag them onto your form, then configure their properties and write handlers for the events that interest you. However, using the Introspector to display information about a Bean is an interesting and educational exercise; see Listing Two.

BeanDumper.dump() is the method that does all the work. First, it tries to create a BeanInfo object; if successful, it calls the methods of BeanInfo that produce information about properties, methods, and events. Introspector.getBeanInfo()'s second argument tells the Introspector where to stop in the inheritance hierarchy. Here, it stops before it parses all the methods from Object, since we're not interested in them. For properties, getPropertyDescriptors() returns an array of PropertyDescriptors. For each PropertyDescriptor, you can call getPropertyType() to find the class of object passed in and out via the property methods. Then, you can extract the pseudonym for each property from the method names with getName(), the method for reading with getReadMethod(), and the method for writing with getWriteMethod(). These last two methods return a Method object that can actually be used to invoke the corresponding method on the object (this is part of reflection).

For the public methods (including the property methods), getMethodDescriptors() returns an array of MethodDescriptors: You can get the associated Method object for each and print out their names.

For the events, getEventSetDescriptors() returns an array of EventSetDescriptors that can be queried to find out the class of the listener, the methods of that listener class, and the add- and remove-listener methods. The BeanDumper program prints out all of this information.

If you invoke BeanDumper on the Frog class like this: java BeanDumper Frog, the output will look like Listing Three.

This reveals most of what the Introspector sees as it produces a BeanInfo object from your Bean. The property type and name are independent. The property name is decapitalized except when it begins with more than one capital letter in a row, and the method names (such as the read and write methods) are produced from a Method object that can be used to invoke the associated method on the object.

The public method list includes both methods associated with a property or event and those that are not, such as croak(). These are all the methods that you can call programmatically for a Bean, and the application-builder tool can list them while you're making method calls, to ease your task.

Finally, you can see that the events are fully parsed out into the listener, its methods and the add- and remove-listener methods. Basically, once you have the BeanInfo, you can find out everything of importance for the Bean. You can also call its methods, even though you don't have any other information except the object itself (again, a feature of reflection).

A More Sophisticated Bean

This next example is slightly more sophisticated, albeit frivolous. It's a canvas that draws a little circle around the mouse whenever it moves. When you press the mouse, the word "Bang!" appears in the middle of the screen, and an action listener is fired.

You can adjust the size of the circle as well as the color, size, and text of the word displayed when you press the mouse. A BangBean also has its own addActionListener() and removeActionListener(), so you can attach your own listener to be fired when the user clicks on the BangBean.

Listing Four shows how BangBean implements the Serializable interface. The application-builder tool can "pickle" all the information for the BangBean using serialization after the program designer has adjusted the property values. When the Bean is created as part of the running application, these pickled properties are restored so you get exactly what you designed.

All the fields are private; normally, access to a Bean is allowed only through methods, usually using the property scheme.

To avoid multithreading issues, addActionListener() is "unicast" -- it only notifies one listener when the event occurs -- and may therefore throw a TooManyListenersException. Normally, you'll use "multicast" events, which notify many listeners. In fact, you should expect all Beans to be run in a multithreaded environment; for more details, see Chapter 14 of "Thinking in Java."

Pressing the mouse puts the text in the middle of the BangBean; if the actionListener field is not null, its actionPerformed() is called. Whenever the mouse is moved, its new coordinates are captured and the canvas is repainted (erasing any text on the canvas).

TestBangBean creates a Frame and places a BangBean within it, attaching a simple ActionListener to the BangBean. Normally, of course, the application-builder tool would create most of the code that uses the Bean.

When you run the BangBean through BeanDumper, you'll notice many more properties and actions. That's because BangBean is inherited from Canvas, and Canvas is itself a Bean, so you see its properties and events as well.

More Complex Bean Support

Creating a Bean can be remarkably simple, but you aren't limited to what I've shown here. The JavaBeans design can scale to more complex situations. There are several ways to add sophistication with properties:

Indexing. Here, I've shown single properties, but it's also possible to represent multiple properties in an array. This is called an "indexed property." You simply provide the appropriate methods and the Introspector recognizes an indexed property so your application-builder tool can respond appropriately.

Binding. A "bound" property notifies other objects via a PropertyChangeEvent. The other objects can then change themselves based on the change to the Bean.

Constraining. Other objects can veto unacceptable changes to a constrained property. The other objects are notified using a PropertyChangeEvent, and they may throw a ProptertyVetoException to prevent the change from happening and to restore the old values.

There are also ways to customize the way your Bean is represented at design time:

  • A custom property sheet for your particular Bean can be automatically invoked when your Bean is selected; all other Beans will use the ordinary property sheet.

  • A custom editor can be automatically invoked for a particular property, instead of the ordinary property sheet.
  • A custom BeanInfo class for your Bean can produce different information than the default created by the Introspector.

It's also possible to turn "expert" mode on and off in all FeatureDescriptors to distinguish between basic features and more complicated ones.

Conclusion

The arrival of JavaBeans on the scene has not been without disruption. For Sun, supporting JavaBeans has meant changes to Java 1.0, such as the new 1.1 event model, plus adding some features such as serialization. However, these changes represent genuine improvements to Java technology and will insure that Java remains an important, strategic platform for years to come.

(Get the source code for this article here.)


Bruce is the author of Thinking in Java (freely available at www.EckelObjects.com/Eckel) from which this article is adapted, Thinking in C++ (Prentice-Hall, 1995), and C++ Inside & Out (Osborne/McGraw-Hill 1993). He is the C++ & Java track chair for the Software Development conference and provides seminars and design consulting in C++ and Java. He can be reached at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.