Into Java - Part VII

From EDM2
Revision as of 12:29, 21 March 2018 by Ak120 (Talk | contribs)

Jump to: navigation, search
Into Java / Part
I II III IV V VI VII VIII IX X XI XII
XIII IV XV XVI XVII XVIII XIX XX XXI XXII XXIII

By Simon Grönlund

In spite of the lovely Swedish summer I will do my best to give you more parts of OOP to build Java applications with. We have already used a number of bricks not yet examined. Hence this column will explain some of them and give us a few more bricks and will move us closer to what I think all of you are longing for - graphical user interface programming.

But to get the hang of GUIs we have to know about interface and event handling of which the latter is used to some extent. And when we are discussing those topics we may pay some attention to inner classes as well.

Upon reading about inner classes you have to be aware of the complexity surrounding this topic. There is much to tell about how they are managed by the compiler, what a "local inner class" is, or what a "static inner class" is. We will be content with looking at anonymous inner classes since you have already used one a few times already. And close to this kind of a class we will examine the adapterconcept.

The result of today's exercises will be a tiny frame with only two buttons able to change the colour of the frame interior. But in fact, this will be a 'small step for you but a major leap for programming'. In the future we will be able to make ourselves much more complex and entertaining applications.

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", and since 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 caught 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.

Java7.gifThe 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

  1. Make a listener object, that implements a listener interface
  2. Make an event source object, that creates event objects when triggered, and add the listener to it
  3. 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