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
|