Chapter 7

Using Inner Classes, Anonymous Classes, and Lambda Expressions

IN THIS CHAPTER

check Using inner classes

check Creating static inner classes

check Implementing anonymous classes

check Adding Lambda expressions

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 types 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 6. In this chapter, I concentrate on the mechanics of creating these types of classes.

You’ll also learn about a feature that was introduced with Java 8 called lambda expressions, which simplify the task of creating and using anonymous classes.

technicalstuff 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 need to know how inner classes and anonymous inner classes work.

Declaring Inner Classes

An inner class is a class that’s declared inside 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, but there’s more to it than that. Here are some key points about inner classes:

  • An inner class automatically has access to all the fields and methods of the outer class, even to private fields and methods. Thus an inner class has more access to its outer class than a subclass has to its superclass. (A subclass can access public and protected members of its superclass, but not private members.)
  • An inner class carries with it a reference to the current instance of the outer class that enables it to access instance data for the outer class.
  • Because of the outer-class instance reference, you can’t create or refer to an inner class from a static method of the outer class. You can, however, create a static inner class, as I describe in the section “Using Static Inner Classes,” later in this chapter.
  • tip One of the main reasons for creating an inner class is to create a class-that’s of interest only to the outer class. As a result, you usually declare inner classes to be private so that other classes can’t access them.

  • Occasionally, code in an inner class needs to refer to the instance of its outer class. To do that, you list the name of the outer class followed by the dot operator and this. If the outer class is named MyOuterClass, for example, you would use MyOuterClass.this to refer to the instance of the outer class.

Viewing an example

Book 3, 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 one-second intervals. 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 three versions of this application. You may want to quickly review Book 3, 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 following paragraphs describe some of the highlights of this program:

  1. →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 access these fields directly.
  2. 11 Because an inner class can be used only 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).
  3. →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 get out of a static context quickly and into an object-oriented mode.

  4. →15 This line is the go method, called from line 12.
  5. →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.
  6. →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.
  7. →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.
  8. →29 This line is the declaration for the inner class named Ticker. Note that this class implements the ActionListener interface.
  9. →33 The actionPerformed method is called by the Timer object every 1,000 milliseconds.
  10. →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 nonstatic fields or methods in its outer class. It can access static fields or methods, however.

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:

  1. →6 The tickMessage field is declared as static. This is necessary so that the static class can access it.
  2. →7 The tockMessage field is also declared as static.
  3. →29 The Ticker class is declared as static.

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. When 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 that a regular class body can include: fields, methods, and 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 create an interface named Ball that has a single method named hit. Then, back in the main method, I declare a variable of type Ball and use 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:

  • You can’t create a constructor for an anonymous class, because the anonymous class doesn’t have a name. What would you call the constructor, anyway?
  • You can’t pass parameters if the anonymous class is based on an interface. That makes sense; interfaces don't have constructors, so Java wouldn’t have anything to pass the parameters to.
  • An assignment statement can use an anonymous class as shown in this example. In that case, the anonymous class body is followed by a semicolon that marks the end of the assignment statement. Note that this semicolon is part of the assignment statement, not the anonymous class. (In the next section, you see an example of an anonymous class that’s passed as a method parameter. In that example, the body isn’t followed by a semicolon.)
  • An anonymous class is a special type of inner class; like any inner class, it automatically has access to the fields and methods of its outer class.
  • An anonymous class can’t be static.

Creating a program 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:

  1. →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.
  2. →19 In the go method, an instance of the Timer class is created.
  3. →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.
  4. →21 This left brace marks the beginning of the body of the anonymous class.
  5. →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.
  6. →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.

Using Lambda Expressions

Java 8 introduces a new feature that in some ways is similar to anonymous classes, but with more concise syntax. More specifically, a lambda expression lets you create an anonymous class that implements a specific type of interface called a functional interface — which has one and only one abstract method.

The Ball interface that was presented in the previous section meets that definition:

interface Ball
{
void hit();
}

Here the only abstract method is the hit method.

technicalstuff A functional interface can contain additional methods, provided they are not abstract. Until Java 8, this was not possible because an interface could contain only abstract methods. However, in Java 8 you can create default methods which provide a default implementation. Thus a functional interface can contain one or more default methods, but can contain only one abstract method. (For more information about default methods, refer to Chapter 5 of this minibook.)

A lambda expression is a concise way to create an anonymous class that implements a functional interface. Instead of providing a formal method declaration that includes the return type, method name, parameter types, and method body, you simply define the parameter types and the method body. The Java compiler infers the rest based on the context in which you use the lambda expression.

The parameter types are separated from the method body by a new operator, called the arrow operator, which consists of a hyphen followed by a greater-than symbol. Here’s an example that implements the Ball interface:

() -> { System.out.println("You hit it!");}

Here the lambda expression implements a functional interface whose single method does not accept parameters. When the method is called, the text "You hit it!" is printed.

You can use a lambda expression anywhere you can use a normal Java expression. You’ll use them most in assignment statements or as passed parameters. The only restriction is that you can use a lambda expression only in a context that requires an instance of a functional interface. For example, here’s a complete program that uses a lambda expression to implement the Ball interface:

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

The general syntax for a lambda expression is this:

(parameters) -> expression

or this:

(parameters) -> { statement;…}

If you use an expression, a semicolon is not required. If you use one or more statements, the statements must be enclosed in curly braces and a semicolon is required at the end of each statement.

Don’t forget that the statement in which you use the lambda expression must itself end with a semicolon. Thus, the lambda expression in the previous example has two semicolons in close proximity:

Ball b = () -> { System.out.println("You hit it!"); };

The first semicolon marks the end of the statement that calls System.out.println; the second semicolon marks the end of the assignment statement that assigns the lambda expression to the variable b.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset