The Peer Interfaces
Each GUI component that AWT provides has a peer. The peer is the implementation of that component in the native environment. For example, the Choice
component in AWT corresponds to some native object that lets the user select one or more items from a list. As a Java developer, you need to worry only about the interface of the Choice
object; when someone runs your program, the Choice
object is mapped to an appropriate native object, which is the Choice
peer, that "does the right thing." You don't really care what the peer is or how it's implemented; in fact, the peer may look (and to some extent, behave) differently on each platform.
The glue that allows an AWT component and its peer to work together is called a peer interface. A peer interface is simply an interface that defines the methods that the peer is required to support. These interfaces are collected in the package java.awt.peer
. For example, this package contains the ButtonPeer
interface, which contains the single method setLabel()
. This means that the native object used to implement a Button
must contain a method called setLabel()
in order for AWT to use it as a button peer. (It's not quite that simple; since a button is also a Component
, the button's peer must also implement the ComponentPeer
interface, which is much more complicated.)
With one exception, there is a one-to-one correspondence between Component
classes and peer interfaces: a Window
has a WindowPeer
, a Checkbox
has a CheckboxPeer
, and so on. The one exception is a new peer interface that appears in Java 1.1: the LightweightPeer
, which doesn't have a corresponding component. The LightweightPeer
is used by components that exist purely in Java, don't have a native peer, and are displayed and managed by another container. LightweightPeer
makes it easier to create new components or containers that can behave like other components, but don't subclass Canvas
or Panel
and don't correspond to anything in the native environment. The best usage I can think of is to subclass Container
to create a lightweight Panel
. If you are only using a Panel
to manage layout, there is no need for a peer to be created to process events. This should result in substantial resource savings where multiple panels need to be created just to help with layout. The following code is all you need to create a LightWeightPanel
:
import java.awt.*; public class LightWeightPanel extends Container { }
There also tends to be a one-to-one relationship between the peer methods and the methods of the Java component. That is, each method in the peer interface corresponds to a method of the component. However, although a peer must implement each method in its peer interface, it doesn't necessarily have to do anything in that method. It's entirely possible for a platform to have a native button object that doesn't let you set the label. In this case, the button peer would implement the setLabel()
method required by the ButtonPeer
interface, but it wouldn't do anything. Of course, the component may also have many methods that don't correspond to the peer methods. Methods that don't correspond to anything in the peer are handled entirely within Java.
The ComponentPeer
interface is the parent of all non-menu objects in the peer package. The MenuComponentPeer
is the parent of all menu objects. The trees mirror the regular object hierarchies. Figure 15.1 shows the object hierarchy diagram.
Figure 15.1: java.awt.peer object hierarchy
Creating a Java component (e.g., Button b = new Button (`Foo`)
) does not create the peer. An object's peer is created when the object's addNotify()
method is called. This is usually when the component's container is added to the screen. The call to a component's addNotify()
method in turn calls the appropriate createxxxx()
method of the Toolkit
(for a Button, createButton()
). Figure 15.2 shows the process.
Figure 15.2: Creating a Button peer
When you remove a component from a container by calling remove()
, the container calls the component's removeNotify()
method. This usually results in a call to the peer's dispose()
method. Depending on the particular component, removeNotify()
may be overridden to perform additional work. Removing a Component
from a Container
does not destroy the Component
. The next time the method addNotify()
is called, the component must be recreated by the peer, with its previous characteristics. For instance, when a TextField
is removed, the current text, plus the start and stop points for the current selection, are saved. These will be restored if you add the text field to its container again. For some components, like a Label
, there is no need to retain any additional information.
A component's peer needs to exist only when the user is interacting with it. If you are developing resource-intensive programs, you might want to consider drawing the components manually when they do not have the focus and using the peer only when they actually have input focus. This technique can save a considerable amount of memory resources but requires extra work on your part as a developer and goes beyond the scope of this tutorial. The LightweightPeer
interface appears to be designed to make this process easier: you could create a dummy button that doesn't do anything and uses the LightweightPeer
. Whenever the mouse enters the button's space, you could quickly remove the dummy button and add a real button.
The peer interfaces are listed in their entirety in the reference section. We won't list them here, primarily because you don't need to worry about them unless you're porting Java to a new platform. Each method in a peer interface corresponds exactly to the similarly named method in the matching component. LightweightPeer
is the only exception, because it doesn't have a matching component, but that's easy to take care of: as you'd expect, LightweightPeer
doesn't define any methods. (Of course, a peer that implements LightweightPeer
would still need to implement the methods inherited from ComponentPeer
, but those are inherited when you subclass Component
.)