[ Previous Section | Chapter Index | Main Index ]

Subsections
Static Nested Classes
Inner Classes
Anonymous Inner Classes
Java 8 Lambda Expressions

Section 5.8

Nested Classes


A class seems like it should be a pretty important thing. A class is a high-level building block of a program, representing a potentially complex idea and its associated data and behaviors. I've always felt a bit silly writing tiny little classes that exist only to group a few scraps of data together. However, such trivial classes are often useful and even essential. Fortunately, in Java, I can ease the embarrassment, because one class can be nested inside another class. My trivial little class doesn't have to stand on its own. It becomes part of a larger more respectable class. This is particularly useful when you want to create a little class specifically to support the work of a larger class. And, more seriously, there are other good reasons for nesting the definition of one class inside another class.

In Java, a nested class is any class whose definition is inside the definition of another class. (In fact, a class can even be nested inside a subroutine, which must, of course, itself be inside a class). Nested classes can be either named or anonymous. I will come back to the topic of anonymous classes later in this section. A named nested class, like most other things that occur in classes, can be either static or non-static.


5.8.1  Static Nested Classes

The definition of a static nested class looks just like the definition of any other class, except that it is nested inside another class and it has the modifier static as part of its declaration. A static nested class is part of the static structure of the containing class. It can be used inside that class to create objects in the usual way. If it is used outside the containing class, its name must indicate its membership in the containing class. That is, the full name of the static nested class consists of the name of the class in which it is nested, followed by a period, followed by the name of the nested class. This is similar to other static components of a class: A static nested class is part of the class itself in the same way that static member variables are parts of the class itself.

For example, suppose a class named WireFrameModel represents a set of lines in three-dimensional space. (Such models are used to represent three-dimensional objects in graphics programs.) Suppose that the WireFrameModel class contains a static nested class, Line, that represents a single line. Then, outside of the class WireFrameModel, the Line class would be referred to as WireFrameModel.Line. Of course, this just follows the normal naming convention for static members of a class. The definition of the WireFrameModel class with its nested Line class would look, in outline, like this:

public class WireFrameModel {

   . . . // other members of the WireFrameModel class
   
   static public class Line {
         // Represents a line from the point (x1,y1,z1)
         // to the point (x2,y2,z2) in 3-dimensional space.
      double x1, y1, z1;
      double x2, y2, z2;
   } // end class Line
   
   . . . // other members of the WireFrameModel class
   
} // end WireFrameModel

The full name of the nested class is WireFrameModel.Line. That name can be used, for example, to declare variables. Inside the WireFrameModel class, a Line object would be created with the constructor "new Line()". Outside the class, "new WireFrameModel.Line()" would be used.

A static nested class has full access to the static members of the containing class, even to the private members. Similarly, the containing class has full access to the members of the nested class, even if they are marked private. This can be another motivation for declaring a nested class, since it lets you give one class access to the private members of another class without making those members generally available to other classes. Note also that a nested class can itself be private, meaning that it can only be used inside the class in which it is nested.

When you compile the above class definition, two class files will be created. Even though the definition of Line is nested inside WireFrameModel, the compiled Line class is stored in a separate file. The name of the class file for Line will be WireFrameModel$Line.class.


5.8.2  Inner Classes

Non-static nested classes are referred to as inner classes. Inner classes are not, in practice, very different from static nested classes, but a non-static nested class is actually associated with an object rather than with the class in which its definition is nested. This can take some getting used to.

Any non-static member of a class is not really part of the class itself (although its source code is contained in the class definition). This is true for inner classes, just as it is for any other non-static part of a class. The non-static members of a class specify what will be contained in objects that are created from that class. The same is true -- at least logically -- for inner classes. It's as if each object that belongs to the containing class has its own copy of the nested class (although it does not literally contain a copy of the compiled code for the nested class). This copy has access to all the instance methods and instance variables of the object, even to those that are declared private. The two copies of the inner class in two different objects differ because the instance variables and methods they refer to are in different objects. In fact, the rule for deciding whether a nested class should be static or non-static is simple: If the nested class needs to use any instance variable or instance method from the containing class, make the nested class non-static. Otherwise, it might as well be static.

In most cases, an inner class is used only within the class where it is defined. When that is true, using the inner class is really not much different from using any other class. You can create variables and declare objects using the simple name of the inner class in the usual way.

From outside the containing class, however, an inner class has to be referred to using a name of the form variableName.NestedClassName, where variableName is a variable that refers to the object that contains the inner class. In order to create an object that belongs to an inner class, you must first have an object that belongs to the containing class. (When working inside the class, the object "this" is used implicitly.)

Looking at an example will help, and will hopefully convince you that inner classes are really very natural. Consider a class that represents poker games. This class might include a nested class to represent the players of the game. The structure of the PokerGame class could be:

public class PokerGame {  // Represents a game of poker.
    
    class Player {  // Represents one of the players in this game.
       .
       .
       .
    } // end class Player
    
    private Deck deck;      // A deck of cards for playing the game.
    private int pot;        // The amount of money that has been bet.
    
    .
    .
    .

} // end class PokerGame

If game is a variable of type PokerGame, then, conceptually, game contains its own copy of the Player class. In an instance method of a PokerGame object, a new Player object would be created by saying "new Player()", just as for any other class. (A Player object could be created outside the PokerGame class with an expression such as "game.new Player()". Again, however, this is rare.) The Player object will have access to the deck and pot instance variables in the PokerGame object. Each PokerGame object has its own deck and pot and Players. Players of that poker game use the deck and pot for that game; players of another poker game use the other game's deck and pot. That's the effect of making the Player class non-static. This is the most natural way for players to behave. A Player object represents a player of one particular poker game. If Player were an independent class or a static nested class, on the other hand, it would represent the general idea of a poker player, independent of a particular poker game.


5.8.3  Anonymous Inner Classes

In some cases, you might find yourself writing an inner class and then using that class in just a single line of your program. Is it worth creating such a class? Indeed, it can be, but for cases like this you have the option of using an anonymous inner class. An anonymous class is created with a variation of the new operator that has the form

          new  superclass-or-interface ( parameter-list ) {
                   methods-and-variables
              }

This constructor defines a new class, without giving it a name, and it simultaneously creates an object that belongs to that class. This form of the new operator can be used in any statement where a regular "new" could be used. The intention of this expression is to create: "a new object belonging to a class that is the same as superclass-or-interface but with these methods-and-variables added." The effect is to create a uniquely customized object, just at the point in the program where you need it. Note that it is possible to base an anonymous class on an interface, rather than a class. In this case, the anonymous class must implement the interface by defining all the methods that are declared in the interface. If an interface is used as a base, the parameter-list must be empty. Otherwise, it can contain parameters for a constructor in the superclass.

Anonymous classes are often used for handling events in graphical user interfaces, and we will encounter them several times in the chapters on GUI programming. For now, we will look at one not-very-plausible example. Consider the Drawable interface, which is defined earlier in this section. Suppose that we want a Drawable object that draws a filled, red, 100-pixel square. Rather than defining a new, separate class and then using that class to create the object, we can use an anonymous class to create the object in one statement:

Drawable redSquare = new Drawable() {
       void draw(Graphics g) {
          g.setColor(Color.RED);
          g.fillRect(10,10,100,100);
       }
   };

Then redSquare refers to an object that implements Drawable and that draws a red square when its draw() method is called. By the way, the semicolon at the end of the statement is not part of the class definition; it's the semicolon that is required at the end of every declaration statement.

Anonymous classes are often used for actual parameters. For example, consider the following simple method, which draws a Drawable in two different graphics contexts:

void drawTwice( Graphics g1, Graphics g2, Drawable figure ) {
    figure.draw(g1);
    figure.draw(g2);
}

When calling this method, the third parameter can be created using an anonymous inner class. For example:

drawTwice( firstG, secondG, new Drawable() {
          void draw(Graphics g) {
             g.drawOval(10,10,100,100);
          }
     } );

When a Java class is compiled, each anonymous nested class will produce a separate class file. If the name of the main class is MainClass, for example, then the names of the class files for the anonymous nested classes will be MainClass$1.class, MainClass$2.class, MainClass$3.class, and so on.


5.8.4  Java 8 Lambda Expressions

The syntax for anonymous classes is cumbersome. In many cases, an anonymous class implements an interface that defines just one method. Java 8 introduces a new syntax that can be used in place of the anonymous class in that circumstance: the lambda expression. Here is what the previous subroutine call looks like using a lambda expression:

drawTwice( firstG, secondG, g -> g.drawOval(10,10,100,100) )

The lambda expression is g -> g.drawOval(10,10,100,100). Its meaning is, "the method that has a parameter g and executes the code g.drawOval(10,10,100,100)." The computer knows that g is of type Graphics because it is expecting a Drawable as the actual parameter, and the only method in the Drawable interface has a parameter of type Graphics. Lambda expressions can only be used in places where this kind of type inference can be made. The general syntax of a lambda expression is

formal-parameter-list  ->  method-body

where the method body can be a single expression, a single subroutine call, or a block of statements enclosed between { and }. When the body is a single expression or function call, the value of the expression is automatically used as the return value of the method that is being defined. The parameter list in the lambda expression does not have to specify the types of the parameters, although it can. Parentheses around the parameter list are optional if there is exactly one parameter and no type is specified for the parameter; this is the form seen in the example above. For a method with no parameters, the parameter list is just an empty set of parentheses. Here are a few more examples of lambda expressions:

() -> System.out.println("Hello World")

g -> { g.setColor(Color.RED); g.drawRect(10,10,100,100); }

(a, b) -> a + b

(int n) -> { 
         while (n > 0) { 
             System.out.println(n); 
             n = n/2; 
          } 
      } // lambda expressions ends here

As you can see, the syntax can still get pretty complicated. There is quite a lot more to say about lambda expressions, but my intention here is only to briefly introduce one of the most interesting new features in Java 8.


End of Chapter 5


[ Previous Section | Chapter Index | Main Index ]