Using Inner Classes

In this chapter, you find out how to use three advanced types of classes: inner classes, static inner classes, and anonymous inner classes. All three are useful in certain circumstances. In particular, inner classes and anonymous inner classes are commonly used with graphical applications created with Swing. For more information about Swing, refer to Book VI. In this chapter, I just concentrate on the mechanics of creating these types of classes.

  TECHNICAL STAUFF 

Once again, this chapter could have a Technical Stuff icon pasted next to every other paragraph. The immediate usefulness of some of the information I present in this chapter may seem questionable. But trust me-you need to know this stuff when you start writing Swing applications. If you want to skip this chapter for now, that's okay. You can always come back to it when you're learning Swing and you need to know how inner classes and anonymous inner classes work.

Declaring Inner Classes

An inner class is a class that's declared inside of another class. Thus the basic structure for creating an inner class is as follows:

class outerClassName { private class innerClassName { // body of inner class } }

The class that contains the inner class is called an outer class. You can use a visibility modifier with the inner class to specify whether the class should be public, protected, or private. This visibility determines whether other classes can see the inner class.

Understanding inner classes

At the surface, an inner class is simply a class that's contained inside another class. However, there's more to it than that. Here are some key points about inner classes:

An example

Book III, Chapter 5 introduces an application that uses the Timer class in the Swing package (javax.swing.Timer) that displays the lines Tick… and Tock… on the console at a one-second interval. It uses a class named Ticker that implements the ActionListener interface to handle the Timer object's clock events.

  Tip 

In this chapter, you see a total of three different versions of this application. You may want to quickly review Book III, Chapter 5 if you're unclear on how this application uses the Timer class to display the Tick… and Tock… messages, or why the JOptionPane dialog box is required.

Listing 7-1 shows a version of this application that implements the Ticker class as an inner class.

Listing 7-1: Tick Tock with an Inner Class

import java.awt.event.*; import javax.swing.*; public class TickTockInner { private String tickMessage = "Tick..."; → 6 private String tockMessage = "Tock..."; → 7 public static void main(String[] args) { TickTockInner t = new TickTockInner(); → 11 t.go(); → 12 } private void go() → 15 { // create a timer that calls the Ticker class // at one second intervals Timer t = new Timer(1000, new Ticker()); → 19 t.start(); // display a message box to prevent the // program from ending immediately JOptionPane.showMessageDialog(null, → 24 "Click OK to exit program"); System.exit(0); → 26 } class Ticker implements ActionListener → 29 { private boolean tick = true; public void actionPerformed(ActionEvent event) → 33 { if (tick) { System.out.println(tickMessage); → 37 } else { System.out.println(tockMessage); → 41 } tick = !tick; } } }

The Observer pattern

Event listeners in Java are part of a Java model called the Delegation Event Model. The Delegation Event Model is an implementation of a more general design pattern called the Observer pattern. This pattern is useful when you need to create objects that interact with each other when a change in the status of one of the objects occurs. The object whose changes are being monitored is called the observable object, and the object that monitors those changes is called the observer object.

The observer object registers itself with the observable object, which then notifies the observer object when its status changes.

You discover more about how Java implements this pattern for event handling in Book VI. But if you're interested, you may want to investigate the Observer and Observable interfaces that are a part of the Java API. They provide a standard way to create simple implementations of the Observer pattern.

The following paragraphs describe some of the highlights of this program:

6

The String variables named tickMessage and tockMessage (line 7) contain the messages to be printed on the console. Note that these variables are defined as fields of the outer class. As you'll see, the inner class Ticker is able to directly access these fields.

11

Because an inner class can only be used by an instantiated object, you can't use it directly from the static main method. As a result, the main method in this program simply creates an instance of the application class (TickTockInner).

12

This line executes the go method of the new instance of the TickTockInner class.

  Tip 

The technique used in lines 11 and 12 is a fairly common programming technique that lets an application quickly get out of a static context and into an object-oriented mode.

Open table as spreadsheet

15

The go method, called from line 12.

19

This line creates an instance of the Timer class with the timer interval set to 1,000 milliseconds (1 second) and the ActionListener set to a new instance of the inner class named Ticker.

24

Here the JOptionPane class is used to display a dialog box. This dialog box is necessary to give the timer a chance to run. The application ends when the user clicks OK.

26

This line calls the exit method of the System class, which immediately shuts down the Java Virtual Machine. This method call isn't strictly required here, but if you leave it out, the timer continues to run for a few seconds after you click OK before the JVM figures out that it should kill the timer.

29

This line is the declaration for the inner class named Ticker. Note that this class implements the ActionListener interface.

33

The actionPerformed method is called by the Timer object every 1,000 milliseconds.

37

In this line and in line 41, the inner class directly accesses a field of the outer class.

Using Static Inner Classes

A static inner class is similar to an inner class, but doesn't require an instance of the outer class. Its basic form is the following:

class outerClassName { private static class innerClassName { // body of inner class } }

Like a static method, a static inner class can't access any non-static fields or methods in its outer class. It can, however, access static fields or methods.

Listing 7-2 shows a version of the Tick Tock application that uses a static inner class rather than a regular inner class.

Listing 7-2: Tick Tock with a Static Inner Class

import java.awt.event.*; import javax.swing.*; public class TickTockStatic { private static String tickMessage = "Tick..."; → 6 private static String tockMessage = "Tock..."; → 7 public static void main(String[] args) { TickTockStatic t = new TickTockStatic(); t.go(); } private void go() { // create a timer that calls the Ticker class // at one second intervals Timer t = new Timer(1000, new Ticker()); t.start(); // display a message box to prevent the // program from ending immediately JOptionPane.showMessageDialog(null, "Click OK to exit program"); System.exit(0); } static class Ticker implements ActionListener →29 { private boolean tick = true; public void actionPerformed( ActionEvent event) { if (tick) { System.out.println(tickMessage); } else { System.out.println(tockMessage); } tick = !tick; } } }

This version of the application and the Listing 7-1 version have only three differences:

6

The tickMessage field is declared as static. This is necessary so that the static class can access it.

7

The tockMessage field is also declared as static.

29

The Ticker class is declared as static.

Open table as spreadsheet

Using Anonymous Inner Classes

Anonymous inner classes (usually just called anonymous classes) are probably the strangest feature of the Java programming language. The first time you see an anonymous class, you'll almost certainly think that someone made a mistake, and that the code can't possibly compile. But compile it does, and it even works. And once you get the hang of working with anonymous classes, you'll wonder how you got by without them.

An anonymous class is a class that's defined on the spot, right at the point where you want to instantiate it. Because you code the body of the class right where you need it, you don't have to give it a name. (That's why it's called an anonymous class.)

Creating an anonymous class

The basic form for declaring and instantiating an anonymous class is this:

new ClassOrInterface () { class-body }

As you can see, you specify the new keyword followed by the name of a class or interface that specifies the type of the object created from the anonymous class. This class or interface name is followed by parentheses, which may include a parameter list that's passed to the constructor of the anonymous class. Then you code a class body enclosed in braces. This class body can include anything a regular class body can include: fields, methods, even other classes or interfaces.

Here's an example of a simple anonymous class:

public class AnonClass { public static void main(String[] args) { Ball b = new Ball() { public void hit() { System.out.println("You hit it!"); } }; b.hit(); } interface Ball { void hit(); } }

In this example, I created an interface named Ball that has a single method named hit. Then, back in the main method, I declared a variable of type Ball and used an anonymous class to create an object. The body of the anonymous class consists of an implementation of the hit method that simply displays the message You hit it! on the console. After the anonymous class is instantiated and assigned to the b variable, the next statement calls the hit method.

When you run this program, the single line You hit it! is displayed on the console.

Here are some things to ponder when you work with anonymous classes:

Tick Tock with an anonymous class

Listing 7-3 shows a more complex example of an anonymous class: a version of the Tick Tock application that uses an anonymous class as the action listener for the timer.

Listing 7-3: Tick Tock with an Anonymous Class

import java.awt.event.*; import javax.swing.*; public class TickTockAnonymous { private String tickMessage = "Tick..."; private String tockMessage = "Tock..."; public static void main(String[] args) → 9 { TickTockAnonymous t = new TickTockAnonymous(); t.go(); } private void go() { // create a timer that calls the Ticker class // at one second intervals Timer t = new Timer(1000, → 19 new ActionListener() → 20 { → 21 private boolean tick = true; public void actionPerformed( → 24 ActionEvent event) { if (tick) { System.out.println(tickMessage); } else { System.out.println(tockMessage); } tick = !tick; } }); → 37 t.start(); // display a message box to prevent the // program from ending immediately JOptionPane.showMessageDialog(null, "Click OK to exit program"); System.exit(0); } }

By now, you've seen enough versions of this program that you should understand how it works. The following paragraphs explain how this version uses an anonymous class as the ActionListener parameter supplied to the Timer constructor:

9

Anonymous classes won't work in a static context, so the main method creates an instance of the TickTockAnonymous class and executes the go method.

19

In the go method, an instance of the Timer class is created.

20

The second parameter of the TimerClass constructor is an object that implements the ActionListener interface. This object is created here via an anonymous class. ActionListener is specified as the type for this class.

21

This left brace marks the beginning of the body of the anonymous class.

24

The actionPerformed method is called every 1,000 milliseconds by the timer. Note that this method can freely access fields defined in the outer class.

37

The right brace on this line marks the end of the body of the anonymous class. Then the right parenthesis marks the end of the parameter list for the Timer constructor. The left parenthesis that's paired with this right parenthesis is on line 19. Finally, the semicolon marks the end of the assignment statement that started on line 19.

Open table as spreadsheet

Категории