Using Inner Classes and Closures - Moving into Advanced Topics - Sams Teach Yourself Java in 24 Hours, 7th Edition (2014)

Sams Teach Yourself Java in 24 Hours, 7th Edition (2014)

Part V: Moving into Advanced Topics

Hour 20. Using Inner Classes and Closures

THIS HOUR’S TO-DO LIST:

Image Add an inner class to an object.

Image Explore why inner classes are useful.

Image Create an anonymous inner class.

Image Use an adapter class with interfaces.

Image Learn why closures have been added in Java 8.

Image Write a lambda expression.

Image Replace an anonymous inner class with a lambda expression.

When it was launched in 1995, the Java programming language was limited in scope and simple to master. There were only around 250 classes in the Java Class Library. The language was designed primarily so that interactive programs called applets could be run inside web browsers. Because no other technology had been introduced to do that, Java made an enormous splash and was quickly embraced by hundreds of thousands of programmers.

As a testament to how well the language was designed, it expanded beyond that one focus and became a general-purpose programming language to rival C++, which at the time was the most popular and widely implemented language on the planet.

Millions of programmers write in Java today as the language nears its 20th birthday. With each new release, Java has supported its existing developers while expanding its capabilities to enable sophisticated new methodologies of software development.

Java 8 introduces an exciting new way to write code called closures, or lambda expressions. These expressions make possible a methodology called functional programming.

During this hour, you learn about lambda expressions after covering two aspects of the language that are prerequisites to using them: inner classes and anonymous inner classes.

Inner Classes

When you create a class in Java, you define the attributes and behavior for that class. The attributes are the class and instance variables that hold data. The behavior is the methods that perform tasks with that data.

A class also can contain something that consists of both attributes and 'margin-top:4.0pt;margin-right:0cm;margin-bottom:4.0pt; margin-left:0cm;line-height:normal'>Inner classes are helper classes that are contained within an enclosing class. You may wonder why they’re necessary, since it’s easy to create a program out of as many new classes as you need. If you’re writing a CheckBook program that needs objects for each check a person writes, you create a Check class. If the program supports recurring monthly payments, add an Autopayment class.

Inner classes aren’t necessary. But when you learn all the things they can do, you’ll find many situations where they’re incredibly handy.

There are three reasons that Java includes inner classes:

1. When a helper class is only used by one other class, it makes sense to define it within that class.

2. They enable a helper class to access private methods and variables it could not access as a separate class.

3. They put a helper class as close as possible to where it is being used in another class.

An inner class is created with the class keyword, like any other class, but it is declared inside the containing class. Typically this is placed with class and instance variables.

Here’s an example of an inner class called InnerSimple being created inside a class called Simple:

public class Simple {

class InnerSimple {
InnerSimple() {
System.out.println("I am an inner class!");
}
}

public Simple() {
// empty constructor
}

public static void main(String[] arguments) {
Simple program = new Simple();
Simple.InnerSimple inner = program.new InnerSimple();
}
}

The inner class is structured like any other class, but placed inside the { and } brackets of the enclosing class.

Creating an inner class requires an object of the outer class. The new operator is called on the object:

Simple.InnerSimple inner = program.new InnerSimple();

The name of the class includes the name of the outer class, a period (.) character, and the inner class name. In the preceding statement, Simple.InnerSimple is the name.

The hour’s first project is a rewrite of an application from Hour 18, “Handling Errors in a Program.” The PageCatalog application from that hour required a helper class called HomePage. The Catalog application in Listing 20.1 revises that project to replace that separate class with an inner class.

Create a new Java file in the package com.java24hours with the name Catalog; then fill it with the source code in Listing 20.1.

LISTING 20.1 The Full Text of Catalog.java


1: package com.java24hours;
2:
3: import java.net.*;
4:
5: public class Catalog {
6: class HomePage {
7: String owner;
8: URL address;
9: String category = "none";
10:
11: public HomePage(String inOwner, String inAddress)
12: throws MalformedURLException {
13:
14: owner = inOwner;
15: address = new URL(inAddress);
16: }
17:
18: public HomePage(String inOwner, String inAddress, String inCategory)
19: throws MalformedURLException {
20:
21: this(inOwner, inAddress);
22: category = inCategory;
23: }
24: }
25:
26: public Catalog() {
27: Catalog.HomePage[] catalog = new Catalog.HomePage[5];
28: try {
29: catalog[0] = new HomePage("Mark Evanier",
30: "http://www.newsfromme.com", "comic books");
31: catalog[1] = new HomePage("Jeff Rients",
32: "http://jrients.blogspot.com", "gaming");
33: catalog[2] = new HomePage("Rogers Cadenhead",
34: "http://workbench.cadenhead.org", "programming");
35: catalog[3] = new HomePage("Juan Cole",
36: "http://www.juancole.com", "politics");
37: catalog[4] = new HomePage("Rafe Colburn",
38: "http://www.rc3.org");
39: for (int i = 0; i < catalog.length; i++) {
40: System.out.println(catalog[i].owner + ": " +
41: catalog[i].address + " -- " +
42: catalog[i].category);
43: }
44: } catch (MalformedURLException e) {
45: System.out.println("Error: " + e.getMessage());
46: }
47: }
48:
49: public static void main(String[] arguments) {
50: new Catalog();
51: }
52: }


The inner class is defined in Lines 6–24. It has two constructors, one that takes a web site owner and site URI beginning in Line 11 and another that takes a site owner, URI, and category beginning in Line 18.

The Catalog class uses this inner class in Line 27, creating an array of HomePage objects. The inner class is referred to as Catalog.HomePage.

The application’s output is shown in Figure 20.1.

Image

FIGURE 20.1 The output of the Catalog application.

Anonymous Inner Classes

A common task you undertake in Java programming is to create a class that will be used only once and has a simple purpose. A special kind of inner class is perfect for this purpose.

An anonymous inner class is one that has no name and is declared and created at the same time.

To use one, you replace a reference to an object with the new keyword, a call to a constructor, and the class definition inside { and } characters.

Here’s some code that does not use an anonymous inner class:

WorkerClass worker = new WorkerClass();
Thread main = new Thread(worker);
main.start();

The worker object presumably implements the Runnable interface and can be run as a thread.

If the code in WorkerClass is short and simple, and the class only needs to be used once, it could be worthwhile to put it in an anonymous inner class. Here’s a new version to do that:

Thread main = new Thread(new Runnable() {
public void run() {
// thread's work to perform goes here
}
);
main.start();

The anonymous inner class has replaced the reference to worker with the following code:

new Runnable() {
public void run() {
// thread's work to perform goes here
}
)

This creates an anonymous class that implements the Runnable interface and overrides the run() method. The statements inside the method would perform whatever work the class requires.

This concept will be easier to comprehend with a full example of how an anonymous inner class is written and why it’s useful.

Once again, a project you created in an earlier hour will be improved through the use of an inner class.

In Hour 15, “Responding to User Input,” you created a Swing application that accepted keyboard input. The KeyViewer application monitored the keyboard with an object that implemented the KeyListener interface—the application’s own class.

A class that implements the interface must implement three methods: keyTyped(), keyPressed(), and keyReleased(). Here’s how they were designed in that program:

public void keyTyped(KeyEvent input) {
char key = input.getKeyChar();
keyLabel.setText("You pressed " + key);
}

public void keyPressed(KeyEvent txt) {
// do nothing
}

public void keyReleased(KeyEvent txt) {
// do nothing
}

The key listener was then set to use the class to monitor keyboard events:

keyText.addKeyListener(this);

With an anonymous inner class and the KeyAdapter class in the java.awt.event package, there’s a better way to create the listener and add it to the graphical user interface.

The KeyAdapter class implements the KeyListener interface with do-nothing implementations of all three methods. It makes it easy to create a listener for keyboard events, because you can create a subclass that only overrides the method that actually does anything.

Here’s the framework of a key listener for the KeyViewer application:

public class KeyViewerListener extends KeyAdapter {
public void keyTyped(KeyEvent input) {
// to do
}
}

This listener could be created and set as a listener:

KeyViewerListener kvl = new KeyViewerListener();
keyText.addKeyListener(kvl);

This approach requires a separate helper class, KeyViewerListener, and an object of that class to be created and assigned to a variable.

Another way to do it is to create the listener as an anonymous inner class:

keyText.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent input) {
char key = input.getKeyChar();
keyLabel.setText("You pressed " + key);
}
});

The listener is created anonymously with the call to new KeyAdapter() followed by the definition of the class. The class overrides the keyTyped() method so that when a key is pressed, it is retrieved with a call to getKeyChar() and displayed by setting the value of keyLabel, which is a JLabel component.


Note

There are other adapter classes in the java.awt.event packages that make it convenient to implement other listeners. The MouseAdapter class has do-nothing methods for three mouse listener interfaces, WindowAdapter implements three window listeners, andFocusAdapter implements FocusListener.


The anonymous inner class does something that a normal helper class could not do: access the keyLabel instance variable. That variable belongs to the KeyViewer class. Inner classes can access the methods and variables of their enclosing class.

In NetBeans, create a new Java file named NewKeyViewer and set com.java24hours as its package. Enter the text of Listing 20.2 into the file and save it when you’re done.

LISTING 20.2 The Full Text of NewKeyViewer.java


1: package com.java24hours;
2:
3: import javax.swing.*;
4: import java.awt.event.*;
5: import java.awt.*;
6:
7: public class NewKeyViewer extends JFrame {
8: JTextField keyText = new JTextField(80);
9: JLabel keyLabel = new JLabel("Press any key in the text field.");
10:
11: public NewKeyViewer() {
12: super("NewKeyViewer");
13: setLookAndFeel();
14: setSize(350, 100);
15: setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
16: keyText.addKeyListener(new KeyAdapter() {
17: public void keyTyped(KeyEvent input) {
18: char key = input.getKeyChar();
19: keyLabel.setText("You pressed " + key);
20: }
21: });
22: BorderLayout bord = new BorderLayout();
23: setLayout(bord);
24: add(keyLabel, BorderLayout.NORTH);
25: add(keyText, BorderLayout.CENTER);
26: setVisible(true);
27: }
28:
29: private void setLookAndFeel() {
30: try {
31: UIManager.setLookAndFeel(
32: "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"
33: );
34: } catch (Exception exc) {
35: // ignore error
36: }
37: }
38:
39: public static void main(String[] arguments) {
40: new NewKeyViewer();
41: }
42: }


The anonymous inner class is created and used in Lines 16–21. It monitors keyboard input using the only method in the KeyListener interface the application requires and displays that input by updating the keyLabel instance variable.

The program’s output is shown in Figure 20.2.

Image

FIGURE 20.2 Monitoring keyboard input with the NewKeyViewer application.

Anonymous inner classes cannot define constructors, so they are more limited than a non-anonymous inner class.

They are a complex feature of the language that are harder to understand as you examine a program’s source code, but they can make a program more concise. Experienced Java programmers often make use of them.

Closures

The new release Java 8 adds the most highly requested language feature in years: closures.

Closures, which also are called lambda expressions, allow an object with a single method to be created with only an -> operator, as long as other conditions are met.

Here’s an example:

Runnable runner = () -> { System.out.println("Run!"); };

This single line creates an object that implements the Runnable interface and makes its run() method equivalent to the following code:

void run() {
System.out.println("Run!");
}

In a lambda expression, the statement to the right of the arrow operator -> becomes the method that implements the interface.

This can be done only when the interface has a single method to implement, such as Runnable, which just contains run(). An interface in Java that has one method is now called a functional interface.

A lambda expression also has something to the left of the arrow operator. In the first example, it’s an empty set of parentheses, and it refers to the arguments sent to the method of the functional interface. Since run() takes no arguments in the Runnable interface, no arguments are required in the expression.

Here’s a second example of a lambda expression that does put something in those parentheses:

ActionListener al = (ActionEvent act) -> {
System.out.println(act.getSource());
}

This is equivalent to the following code in an object that implements the ActionListener interface in the java.awt.event package:

public void actionPerformed(ActionEvent act) {
System.out.println(act.getSource());
}

The ActionListener interface receives action events, such as when a user clicks a button. The only method in the functional interface is actionPerformed(ActionEvent). The argument is an ActionEvent object that describes the user action that triggered the event.

The right half of the lambda expression defines the actionPerformed() method as a single statement that displays information about the user interface component that triggered the event.

The left half of the expression declares that an ActionEvent object named act is the argument to the method.

This object, act, is used inside the body of the method. The left-half reference to act appears to be outside the scope of the right-half implementation of the method. This is because closures allow code to refer to the variables of another method outside the scope of those variables.

As you can see, one effect of lambda expressions is that they shorten code. A single expression creates an object and implements an interface.

This new feature of Java can make the code even shorter through the language’s support for target typing.

In a lambda expression, it’s possible to infer the class of the argument or arguments sent to the method. Consider the last example. Because the ActionListener functional interface has a method that takes an ActionEvent object as its only argument, the name of that class can be omitted.

Here’s a revised version of the lambda expression taking this into account:

ActionListener al = (act) -> {
System.out.println(act.getSource());
}

The next two programs that you create should provide a more concrete demonstration of the difference wrought by the introduction of closures to Java.

The ColorFrame application in Listing 20.3 is a graphical Swing program that displays three buttons to change the color of the frame. This version of the application uses an anonymous inner class, not a lambda expression, to monitor user clicks on the three buttons.

Create a new program in NetBeans with that name and com.java24hours as the package. Enter the text of the listing.

LISTING 20.3 The Full Text of ColorFrame.java


1: package com.java24hours;
2:
3: import java.awt.*;
4: import java.awt.event.*;
5: import javax.swing.*;
6:
7: public class ColorFrame extends JFrame {
8: JButton red, green, blue;
9:
10: public ColorFrame() {
11: super("ColorFrame");
12: setLookAndFeel();
13: setSize(322, 122);
14: setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
15: FlowLayout flo = new FlowLayout();
16: setLayout(flo);
17: red = new JButton("Red");
18: add(red);
19: green = new JButton("Green");
20: add(green);
21: blue = new JButton("Blue");
22: add(blue);
23: // begin anonymous inner class
24: ActionListener act = new ActionListener() {
25: public void actionPerformed(ActionEvent event) {
26: if (event.getSource() == red) {
27: getContentPane().setBackground(Color.RED);
28: }
29: if (event.getSource() == green) {
30: getContentPane().setBackground(Color.GREEN);
31: }
32: if (event.getSource() == blue) {
33: getContentPane().setBackground(Color.BLUE);
34: }
35: }
36: };
37: // end anonymous inner class
38: red.addActionListener(act);
39: green.addActionListener(act);
40: blue.addActionListener(act);
41: setVisible(true);
42: }
43:
44: private void setLookAndFeel() {
45: try {
46: UIManager.setLookAndFeel(
47: "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"
48: );
49: } catch (Exception exc) {
50: // ignore error
51: }
52: }
53:
54: public static void main(String[] arguments) {
55: new ColorFrame();
56: }
57: }


This program is shown running in Figure 20.3.

Image

FIGURE 20.3 Monitoring action events with an anonymous inner class.

Lines 24–36 of the application create an event listener for the ColorFrame class using an anonymous inner class. It’s an object of no name that implements the ActionListener interface’s only method, actionPerformed(ActionEvent).

Within that method, the frame’s content pane is retrieved by calling its getContentPane() method. Anonymous inner classes have access to the methods and instance variables of their enclosing class. A separate helper class would not.

The content pane’s setBackground(Color) method changes the frame’s background to that color. The color of the three buttons does not change.

Now take a look at the NewColorFrame application in Listing 20.4. Create a new program in NetBeans and enter the text of this listing.

LISTING 20.4 The Full Text of NewColorFrame.java


1: package com.java24hours;
2:
3: import java.awt.*;
4: import java.awt.event.*;
5: import javax.swing.*;
6:
7: public class NewColorFrame extends JFrame {
8: JButton red, green, blue;
9:
10: public NewColorFrame() {
11: super("NewColorFrame");
12: setLookAndFeel();
13: setSize(322, 122);
14: setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
15: FlowLayout flo = new FlowLayout();
16: setLayout(flo);
17: red = new JButton("Red");
18: add(red);
19: green = new JButton("Green");
20: add(green);
21: blue = new JButton("Blue");
22: add(blue);
23: // begin lambda expression
24: ActionListener act = (event) -> {
25: if (event.getSource() == red) {
26: getContentPane().setBackground(Color.RED);
27: }
28: if (event.getSource() == green) {
29: getContentPane().setBackground(Color.GREEN);
30: }
31: if (event.getSource() == blue) {
32: getContentPane().setBackground(Color.BLUE);
33: }
34: };
35: // end lambda expression
36: red.addActionListener(act);
37: green.addActionListener(act);
38: blue.addActionListener(act);
39: setVisible(true);
40: }
41:
42: private void setLookAndFeel() {
43: try {
44: UIManager.setLookAndFeel(
45: "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"
46: );
47: } catch (Exception exc) {
48: // ignore error
49: }
50: }
51:
52: public static void main(String[] arguments) {
53: new NewColorFrame();
54: }
55: }


The NewColorFrame application implements the action listener in lines 24–34. You don’t need to know the name of the method in the ActionListener interface to use it in the program and don’t need to specify the class of the ActionEvent.

Lambda expressions support functional programming, a methodology for software design that has until now been unavailable to Java programmers.

This section has introduced the basic syntax of lambda expressions and two of the more common ways they will be employed in programs.

But functional programming in Java is a topic so powerful and revolutionary that it’s the subject of entire books.

At this point, you should be able to recognize lambda expressions and use them to implement any single-method interface, also called a functional interface.

Summary

Inner classes, anonymous inner classes, and lambda expressions are among the most complex parts of the Java language to learn. They appeal primarily to programmers who have been writing programs in the language for a while and can take advantage of powerful features to accomplish more in fewer lines of code.

But even before you reach a point where you’re writing your own lambda expressions, you should be able to benefit from the use of inner classes and anonymous inner classes.

A non-anonymous inner class is structured like a separate helper class, but it’s placed inside another class alongside the instance variables, class variables, instance methods, and class methods. Unlike a helper class, it can access the private variables and methods of the class that it is created inside.

An anonymous inner class removes the need to devote an object to a class that’s only used once, such as an event listener attached to a user interface component in Swing.

Lambda expressions are deceptively similar in appearance, requiring only a new arrow operator -> to create. But the implications of what they enable a Java programmer to do are enormous. Programmers with experience in other languages have been clamoring for this functionality for years.

Workshop

Q&A

Q. Anonymous inner classes are confusing. Do I have to use them in my programs?

A. No. As with other complex features of the Java language, you don’t have to use them if you can get the job done some other way.

But you should learn about them anyway, because you’re likely to encounter them in Java code.

When you look at the Java programs written by experienced programmers, you often find both inner classes and anonymous inner classes. So even if you’re not ready to create them yourself, it’s worthwhile to learn what they are and how they work.

Q. Are the Hatfields and McCoys still feuding?

A. The West Virginia and Kentucky families are on good terms 124 years after the last casualty in their infamous 35-year conflict.

In 1979, Hatfields and McCoys got together to play the TV game show Family Feud for a week. A pig was kept on stage and awarded to the winning family.

In 2003, a formal peace treaty was reached between the families in Pikeville, KY.

The Hatfield-McCoy Trails, 500 miles of trails for recreational off-road driving, were established in West Virginia in 2000 and expanded over the next decade.

Quiz

To see whether you should go to the front of the class with the knowledge you’ve attained during this hour, answer the following questions about inner classes, anonymous inner classes, and lambda expressions.

1. What makes some inner classes anonymous?

A. They implement an interface.

B. They aren’t given a name.

C. Both.

2. What’s another name for an interface that contains only one method?

A. Abstract interface

B. Class

C. Functional interface

3. What is it called when a lambda expression guesses the class of an argument to a method?

A. Target typing

B. Type casting

C. Class inference

Answers

1. C. An anonymous inner class implements an interface with the new keyword, skipping the creation and naming of a class that implements it.

2. C. Functional interface, as of Java 8. They were called single abstract method interfaces in earlier versions of the language.

3. A. Target typing can infer the class of any functional interface method argument.

Activities

To finish this hour with class, put its subjects to work with the following activities:

Image Rewrite the LottoEvent class from Hour 16, “Building a Complex User Interface,” to use a lambda expression for the event listener.

Image Write a Swing application that rolls five six-sided dice at the click of a button. Use a lambda expression for the event listener and an inner class named Dice for the die.

To see Java programs that implement these activities, visit the book’s website at www.java24hours.com.