Interface
A few columns ago I stated that Java
does not allow inheritance from more than one other class. This way we avoid
the dangers of multiple inheritance, the times you do not know exactly what is taking
place or which super class is in use at a precise moment. A few programmers miss
this dangerous feature--multiple inheritance--but Java offers a more robust and
sound feature that we will examine, the interface.
An interface is simply a promise that your class will implement the methods as they
are signed in the interface. You tell the compiler by using the keyword implements.
Using interfaces you avoid not knowing how the other classes are working, you implement
the method(s) in the actual class the way you like, and well in your hand. And that
will be true for each interface you implement in a class, there is no limit to how
many interfaces you may implement.
Your class may implement these methods any way you like, but anyway the signature
must be correct. Let us look at one of the most commonly used interfaces. That is ActionListener that
is frequently used in GUI applications. You find ActionListener in the package java.awt.event,
please look it up in your Java API. The source code looks like
/*
* @(#)ActionListener.java 1.10 98/09/21
*
* Copyright 1996-1998 by Sun Microsystems, Inc.,
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of Sun Microsystems, Inc. ("Confidential Information"). You
* shall not disclose such Confidential Information and shall use
* it only in accordance with the terms of the license agreement
* you entered into with Sun.
*/
package java.awt.event;
import java.util.EventListener;
/**
* The listener interface for receiving action events.
* The class that is interested in processing an action event
* implements this interface, and the object created with that
* class is registered with a component, using the component's
* <code>addActionListener</code> method. When the action event
* occurs, that object's <code>actionPerformed</code> method is
* invoked.
*
* @see ActionEvent
* @see <a href="http://java.sun.com/docs/books/tutorial/post1.0 /ui/eventmodel.html">
Tutorial: Java 1.1 Event Model</a>
* @see <a href="http://www.awl.com/cp/javaseries/jcl1_2.html">
Reference: The Java Class Libraries (update file)</a>
*
* @version 1.10 09/21/98
* @author Carl Quinn
*/
public interface ActionListener extends EventListener
{
/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e);
}
|
|
As seen when peeling the comments away the interface is only five lines long,
so here is the short for them
package java.awt.event;
import java.util.EventListener;
public interface ActionListener extends EventListener
{
public void actionPerformed(ActionEvent e);
}
|
|
We do not have to explore EventListener since it is even more sparse than ActionListener is,
though it is the root of every listener interface.
Out of interest in this code is that it does not contain nothing more than outlining
of the method actionPerformed(ActionEvent e) that has to be implemented in some way in
the classes implementing the ActionListener interface. The interface does not spell out
how the method shall be written, it may be an empty method if you like to throw
the events away. But the signature must be (ActionEvent
e), and returning nothing.
You may not try to use no parameter, a parameter of the wrong type or getting a
return value from a void method. Your error will be found by javac so no harm is
done, except that your blood pressure will increase.
An empty method is simply a method that
may look like:
public void myMethod(int i) {}
That is, the curly braces contain nothing
to do, the block is empty.
|
More complex interfaces might force
you to implement many more methods, but still, the interface looks this tiny and
slim. One example of a more complex interface is java.awt.event.MouseListener
that you might look up and
read. There you have to implement five methods, and most of them you may decide
to implement as empty methods.
The conclusion of implementing an interface is: You promise to implement the methods
of the interface. You do that anyway you like to, but they shall be implemented,
and implemented with the correct signature.
What is the gain then? Ahem, as implied this is pseudo inheritance, without the
dangers coming by multiple inheritance. The gain is that we tell the compiler, and
others reading our code, that we would like to use "multiple inheritance",
andsince we at the same time make a promise we will not forget to implement necessary
methods. Hence, interfaces makes our code more readable and forces ourselves to
implement essential methods. Finally, we have full control over these methods.
Interfaces may be used to hold static constants,
too. Please, look up javax.swing.SwingConstants for an example. You access these static constants
by for example SwingConstants.HORIZONTAL (with path if necessary), as you do with any
object variable. Note that static constants are spelled with capitals for readability,
as outlined in the SUN Java Coding Style.
Is it possible to create your own interface? Of course, whenever you find a good
reason to, you are perfectly free to implement an interface for both methods and/or
static constants. The syntax is as shown above, but we make an example here
public interface MyInterface {
public static final int NORTH = 0; // by degrees
public static final int EAST = 90;
public static final int SOUTH = 180;
public static final int WEST = 270;
public void whishedMethod(Parameter p);
}
|
|
Adapters
In spite of most interfaces contain
only one or a few methods to implement, some have plenty more to take care of. Two
examples that I wouldn't like to implement are AccessibleComponent
and
TreeSelectionModel that
have 27 methods each. Fortunately we do not use these interfaces that often, but
you will find some interfaces tedious anyway since you will use them that frequently.
Is there any way you do not have to implement that many methods each time you like
to use these interfaces?
In the frames we have used in earlier columns we have used the shortcut you are
looking for. Look at the code below, and, please, rewrite it in an editor of your
choice since we will extend this code to the little application we will develop
together.
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
public class FirstButtonFrame extends JFrame {
/* the constructor of this class */
public FirstButtonFrame() {
/* JFrame methods */
setTitle("FirstButtonFrame");
setLocation(100, 50);
setSize(250, 100);
addWindowListener(new WindowAdapter()
{
public
void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
/* merely the driver */
public static void main(String[] args) {
FirstButtonFrame frame = new
FirstButtonFrame();
frame.show();
}
}
|
|
In the latter part of the constructor we see how we are adding a window listener.
That is, we add something that will listen to any events we cause to the frame,
by clicking the closing X or other events. Looking up
java.awt.event.WindowAdapter in
the Java API will show us that WindowAdapter is implementing WindowListener. WindowListener is
an interface with seven methods to implement but using this convenience adapter
we only have to implement the method of interest, windowClosing. In fact we have now made ourselves an anonymous
inner class (that will be discussed in a moment).
What happened really? Let us look at the code line by line. We add a window listener
that this time will be the convenient WindowAdapter, hence the new WindowAdapter().
But what is taking place after that? A curly brace encloses a block of all the methods
you like to implement yourself, this time we only implement the public void windowClosing that
takes a WindowEvent as
parameter. The other six methods we do not touch and thus they will remain as they
are implemented in the WindowAdapter class, that is they are empty. Our implementation
of the windowClosing method
is a plain request to the system to quit the application cleanly.
Adapters are used as convenience classes to spare us the tedious work of implementing
many methods from interfaces, all of these implemented methods of adapters are empty,
free to implement or to let be. You are not allowed to add variables within the
"constructor block" though. Finally, adapters exist for every listener
interface that have more than one method, at least as far as I am aware of.
Inner classes
Most inner classes are classes that
you hide within another class, within its borders. As an example, you might want
to do a cannon ball game and you have implemented a special class that will react
upon certain user response, as mouse clicks. This class you can easily hide within
the borders of the main class of the game. Mostly such inner classes are listener
classes, but inner classes may be any kind of class.
The profit of inner classes is that they can easily use data fields of the surrounding
object. And further an inner class may be anonymous as seen above. As long as we
do not need a reference to a certain object for later access we may chose to make
an anonymous object. Upon compiling such code you will see that an unusual class
appears in your listing
-
FirstButtonFrame$1.class
-
FirstButtonFrame.class
-
FirstButtonFrame.java
The first line show us the anonymous
class name, the context class name with a $1 added. My writings about these inner
classes and anonymous such are merely at a premium, but as with any knowledge: it
is hard to retrieve but has no weight when you later carry it.
Event handling
A basic concept in any GUI programming
of today is the event handling model, mostly explained in a way as the image shows.
Most of the time the application will wait for user input, that raise an event that
is catched by a listener that will examine the event object created (this is not
always necessary, compare with our WindowAdapter that do not examine the WindowEvent
but only calls the exit routine) and start some preset action.
The actors necessary are:
-
Event sources
-
Listener(s)
-
Event objects
The event source may be a JButton. Whenever
pressed you can imagine that it causes some kind of reaction within the computer.
Any event source do that. They create an event object and send that object to any
registered listener. So far we ask, what is that event object? And what is a registered
listener?
We start with the listener. Any listener in the Java community implements one of
the listener interfaces available. So, a class promises that it will implement one
or more interfaces and each of those implementations, the implemented methods, acts
as a listener to certain event objects.
As with subscriptions, for example WarpCast news, a subscriber has to request e-mail
news with the WarpCast server, else no e-mail will arrive. But after a subscription
is successfully processed, each time a notice is posted it will arrive in your mail-box.
Meanwhile you just sit and wait. In a similar way you have to add the listener object
with the event source object.
That is, first you create the listener object implementing a suitable listener interface.
Then you create an event source object, maybe a JButton, and add the action listener
you created a moment ago to the subscription list of the event source object. After
that the application will wait for user input, and when that happens an EventObject,
or probably one of its subclasses, is created and sent to the listener objects at
the list of this event source object. Mostly it is only one listener subscribed.
The method catching the event object of the action performed will know what to do,
that is the method you had to implement because of the promise made through the
use of the interface. Often this method will be added to many different subscription
lists and hence have to examine what to do, exactly as you maybe have subscribed
to different mailing lists and probably have set the filter mechanism of your one
e-mail client to handle them differently.
What are the EventObjects then? Quoted from the Java API: "All Event's are
constructed with a reference to the object, the "source", that is logically
deemed to be the object upon which the Event in question initially occurred upon."
Yes, they are objects containing information that you might need whenever choosing
what to do after the user input. For example, a MouseEvent object can give you the
position of the mouse when a button was clicked, how many times it was clicked and
upon which button of the common three mouse buttons. An ActionEvent from a JButton
merely know which button was triggered and if any of the modifier keys were held
down during this action event.
Let us add to the code we started out with. We will add one button and see what
else is necessary then.
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
public class FirstButtonFrame extends JFrame implements ActionListener {
private JPanel panel;
/* the constructor of this class */
public FirstButtonFrame() {
/* JFrame methods */
setTitle("FirstButtonFrame");
setLocation(100, 50);
setSize(250, 100);
addWindowListener(new WindowAdapter()
{
public void
windowClosing(WindowEvent e) {
System.exit(0);
}
});
panel = new JPanel();
JButton butt = new JButton("White");
panel.add(butt);
butt.addActionListener(this);
butt.setActionCommand("Button
1");
Container pane = getContentPane();
pane.add(panel, "North");
}
/* merely the driver */
public static void main(String[] args) {
FirstButtonFrame frame = new
FirstButtonFrame();
frame.show();
}
}
|
|
Since JButtons creates ActionEvent we states that we will implement the ActionListener interface.
Please note that in the code above we have still not done that, we have not kept
our promise to implement the interface yet. Please, try to compile the class now
and you will get the most weird error message, obviously javac got lost and tells
you what might be a reasonable conclusion. Anyway, javac tells you that the class
'does not define the method void actionPerformed(java.awt.event.ActionEvent) from
interface java.awt.event.ActionListener', that is correct, we have not. Please hold
on a moment, and let us look at the other new lines first.
We know that we will refer to the panel used to hold the buttons later on and thus
declares the panel as
a private class variable of JPanel type. Within the constructor we then instantiate
the panel as
a new JPanel.
Continuing with the button we create a JButton with the visible title "White".
It will automatically be instantiated with the proper size and the given title.
Because we never have to refer to the button later on we do not need to declare
it as a class variable. It is not forbidden, but why declare class variables when
we do not need to? That will only fill the class with scrawl, and make it hard to
read. The panel will
remember what is added to it, and that is enough.
Now the first step with event handling comes, adding an action listener. We have
both an event source object, the JButton butt, and an object that promises to act as a
listener, this time it is the context itself and hence we add
this as the listening object.
The last line of the new lines might seem mysterious, butt.setActionCommand("Button
1"). In a later column
we will discuss how to take powerful advantage of this feature, but for now we leave
that with a short explanation; if no actionCommand is specified then JButton will use the text
of the button as the actionCommand. But whenever you specifically set a command, that
one will be used and remembered. We will look to that in a few moments.
The last two lines are not new to us, we simply add the
panel to the contentPane of
the frame.
The promise
We promised to implement the interface
ActionListener and thus we will add a method for that. The method has to have the
following signature since that is specified in the interface:
public void actionPerformed(ActionEvent e)
In fact, the only thing we might change
is the name of the parameter variable, if it is a lengthy method we will make, you
might chose a longer variable name, like evt or event. But except the parameter name, every letter
have to be untouched. The code will look like
public void actionPerformed(ActionEvent
evt) {
String cmd = evt.getActionCommand();
System.out.print(cmd + "\t");
// writes cmd and a tab
if (cmd.equals("Button 1"))
{
// doButtonOne();
<=== has to be added, too
} else {
System.out.println("Any
error.");
}
}
|
|
Now you may compile the code and it works, but not much happens since we commented
out the doButtonOne() method
call. Anyway there is some output to the terminal window telling us "Button
1", so something is going on. The connection was, we made an object of the
class and told it to show. Within the constructor block--constructors can be really
long and nasty--we created a button object and added the class object as an action
listener. Thereafter, any time you press the button an ActionEvent object is created
and sent to the listener that loyally implemented the
actionPerformed method,
which take care of that object and simply asks it for the actionCommand we set to
the button.
Since the actionCommand is always a text string we easily can test it against other
text strings, but recall we have to make use of the method
equals, since the two text
strings certainly are not the same object and hence the == test will not work. We
merely print the action command string and then do a test. Let us add the doButtonOne method
after the actionPerformed code.
private void doButtonOne() {
System.out.println("is \"White\".");
panel.setBackground(Color.white);
}
|
|
To prohibit illegal calls to this method we hide it using
private and it will be visible
only within the surrounding object. The method simply prints a small message, an
extension to the former print made in the actionPerformed
code. After that the panel
background is set to white. Please, remember to the remove the comment marks // from the
actionPerformed code,
as well as the trailing comment at that line.
You might think that it is not necessary to make special methods for the actions
taking place within doButtonOne(), and you are right of course. Only two lines do not
justify a method of their own, so simply view this as an example code. It is, on
the contrary, most common with lengthy and complex actions taking place after a
user input. Thus it is wise to get the habit of using helper methods right away.
With wisely chosen method names the code will be easily read as well, 'if this is
true then this will happen, aha!'.
The next step will of course be to add another button. The final code will look
like this
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
public class FirstButtonFrame extends JFrame implements ActionListener {
private JPanel panel;
/* the constructor of this class */
public FirstButtonFrame() {
/* JFrame methods */
setTitle("FirstButtonFrame");
setLocation(100, 50);
setSize(250, 100);
addWindowListener(new WindowAdapter()
{
public void
windowClosing(WindowEvent e) {
System.exit(0);
}
});
panel = new JPanel();
JButton butt = new JButton("White");
panel.add(butt);
butt.addActionListener(this);
butt.setActionCommand("Button
1");
butt = new JButton("Cyan");
panel.add(butt);
butt.addActionListener(this);
butt.setActionCommand("Button
2");
Container pane = getContentPane();
pane.add(panel, "North");
}
public void actionPerformed(ActionEvent evt) {
String cmd = evt.getActionCommand();
System.out.print(cmd + "\t");
// writes cmd and a tab
if (cmd.equals("Button 1"))
{
doButtonOne();
} else if (cmd.equals("Button
2")) {
doButtonTwo();
} else {
System.out.println("Any
error.");
}
}
private void doButtonOne() {
System.out.println("is \"White\".");
panel.setBackground(Color.white);
}
private void doButtonTwo() {
System.out.println("is \"Cyan\".");
panel.setBackground(Color.cyan);
}
/* merely the driver */
public static void main(String[] args) {
FirstButtonFrame frame = new
FirstButtonFrame();
frame.show();
}
}
|
|
The only peculiar thing with the code
added is that we reuse the local variable name butt. That is OK since we do not like to hold
references to any of the buttons. In fact, as soon as the constructor block is finished
the local name butt will
disappear and no longer exist. The panel holds the buttons and will remember their
existence, but not their former and temporary names. If this coding style annoys
you, you might use a new local variable name each new item created, but with several
buttons or other items that seems to be a tedious work.
Of course there are other small and new things in the code. An example of new things
might be the \" that
causes Java to print a quotation mark ". But if there is anything, please
use the forum and ask the question. I will answer any question I am able to answer
about the Java columns.
Summary
Today we have looked at anonymity, and
not only at anonymous inner classes but other minor objects, too. We have not used
any inner class that we made ourselves but we have touched the idea though.
The main part of today's column was the event model, and if this seemed much to
learn, stay rested. We will return to this topic many times and since we will continue
with GUI programming we will discuss it over and over again. Anyway, you have to
-
Make a listener object, that implements
a listener interface
-
Make an event source object, that creates
event objects when triggered, and add the listener to it
-
Catch the event objects in a properly
implemented method, maybe examine the message it conveys
Now I will leave town and go fishing,
go pick some berries or mushrooms and enjoy the wonderful creation found in the
nature. See you next month.
The FirstButtonFrame.java
|