Before you begin

This unit is part of the “Intro to Java programming” learning path. Although the concepts discussed in the individual units are standalone in nature, the hands-on component builds as you progress through the units, and I recommend that you review the prerequisites, setup, and unit details before proceeding.

Unit objectives

  • Know how to define nested classes and when it’s appropriate to use them
  • Understand the side effects of using nested classes
  • Comprehend the special use of new operator with nested classes
  • Know how and when to use static inner classes and anonymous inner classes

Where to use nested classes

As its name suggests, a nested class (or inner class) is a class defined within another class:


public class EnclosingClass {
  . . .
  public class NestedClass {
  . . .

  }
}

Like member variables and methods, Java classes can also be defined at any scope including public, private, or protected. Nested classes can be useful when you want to handle internal processing within your class in an object-oriented fashion but limit the functionality to the class where you need it.

Typically, you use a nested class when you need a class that’s tightly coupled with the class in which it’s defined. A nested class has access to the private data within its enclosing class, but this structure carries with it side effects that aren’t obvious when you start working with nested classes.

Scope in nested classes

Because a nested class has scope, it’s bound by the rules of scope. For example, a member variable can only be accessed through an instance of the class (an object). The same is true of a nested class.

Suppose you have the following relationship between a Manager and a nested class called DirectReports, which is a collection of the Employees that report to that Manager:


public class Manager extends Employee {
  private DirectReports directReports;
  public Manager() {
    this.directReports = new DirectReports();
  }
  . . .
  private class DirectReports {
  . . .
  }
}

Just as each Manager object represents a unique human being, the DirectReports object represents a collection of actual people (employees) who report to a manager. DirectReports differ from one Manager to another. In this case, it makes sense that I would only reference the DirectReports nested class in the context of its enclosing instance of Manager, so I’ve made it private.

Public nested classes

Because it’s private, only Manager can create an instance of DirectReports. But suppose you wanted to give an external entity the ability to create instances of DirectReports. In this case, it seems like you could give the DirectReports class public scope, and then any external code could create DirectReports instances, as shown in Listing 1.

Listing 1. Creating DirectReports instances: First attempt

public class Manager extends Employee {
  public Manager() {
  }
  . . .
  public class DirectReports {
  . . .
  }
}
//
public static void main(String[] args) {
  Manager.DirectReports dr = new Manager.DirectReports();// This won't work!
}

The code in Listing 1 doesn’t work, and you’re probably wondering why. The problem (and also its solution) lies with the way DirectReports is defined within Manager, and with the rules of scope.

The rules of scope, revisited

If you had a member variable of Manager, you’d expect the compiler to require you to have a reference to a Manager object before you could reference it, right? Well, the same applies to DirectReports, at least as it’s defined in Listing 1.

To create an instance of a public nested class, you use a special version of the new operator. Combined with a reference to an enclosing instance of an outer class, new gives you a way you to create an instance of the nested class:


public class Manager extends Employee {
  public Manager() {
  }
  . . .
  public class DirectReports {
  . . .
  }
  }
// Meanwhile, in another method somewhere...
public static void main(String[] args) {
  Manager manager = new Manager();
  Manager.DirectReports dr = manager.new DirectReports();
}

Note on line 12 that the syntax calls for a reference to the enclosing instance, plus a dot and the new keyword, followed by the class you want to create.

Static inner classes

At times, you want to create a class that’s tightly coupled (conceptually) to a class, but where the rules of scope are somewhat relaxed, not requiring a reference to an enclosing instance. That’s where static inner classes come into play. One common example is to implement a Comparator, which is used to compare two instances of the same class, usually for the purpose of ordering (or sorting) the classes:


public class Manager extends Employee {
  . . .
  public static class ManagerComparator implements Comparator<Manager> {
  . . .
  }
  }
// Meanwhile, in another method somewhere...
public static void main(String[] args) {
  Manager.ManagerComparator mc = new Manager.ManagerComparator();
  . . .
}

In this case, you don’t need an enclosing instance. Static inner classes act like their regular Java class counterparts, and you should use them only when you need to couple a class tightly with its definition. Clearly, in the case of a utility class like ManagerComparator, creating an external class is unnecessary and potentially clutters up your code base. Defining such classes as static inner classes is the way to go.

Anonymous inner classes

With the Java language, you can implement abstract classes and interfaces pretty much anywhere, even in the middle of a method if necessary, and even without providing a name for the class. This capability is basically a compiler trick, but there are times when anonymous inner classes are handy to have.

Listing 2 builds on the example in Listing 1 in Unit 17: Interfaces, adding a default method for handling Employee types that are not StockOptionEligible. The listing starts with a method in HumanResourcesApplication to process the stock options, followed by a JUnit test to drive the method.

Listing 2. Handling Employee types that are not StockOptionEligible

// From HumanResourcesApplication.java
public void handleStockOptions(final Person person, StockOptionProcessingCallback callback) {
  if (person instanceof StockOptionEligible) {
    // Eligible Person, invoke the callback straight up
    callback.process((StockOptionEligible)person);
  } else if (person instanceof Employee) {
    // Not eligible, but still an Employee. Let's cobble up a
    /// anonymous inner class implementation for this
    callback.process(new StockOptionEligible() {
      @Override
      public void awardStockOptions(int number, BigDecimal price) {
        // This employee is not eligible
        log.warning("It would be nice to award " + number + " of shares at $" +
            price.setScale(2, RoundingMode.HALF_UP).toPlainString() +
            ", but unfortunately, Employee " + person.getName() + 
            " is not eligible for Stock Options!");
      }
    });
  } else {
    callback.process(new StockOptionEligible() {
      @Override
      public void awardStockOptions(int number, BigDecimal price) {
        log.severe("Cannot consider awarding " + number + " of shares at $" +
            price.setScale(2, RoundingMode.HALF_UP).toPlainString() +
            ", because " + person.getName() + 
            " does not even work here!");
      }
    });
  }
}
// JUnit test to drive it (in HumanResourcesApplicationTest.java):
@Test
public void testHandleStockOptions() {
  List<Person> people = HumanResourcesApplication.createPeople();

  StockOptionProcessingCallback callback = new StockOptionProcessingCallback() {
    @Override
    public void process(StockOptionEligible stockOptionEligible) {
      BigDecimal reallyCheapPrice = BigDecimal.valueOf(0.01);
      int numberOfOptions = 10000;
      stockOptionEligible.awardStockOptions(numberOfOptions, reallyCheapPrice);
    }
  };
  for (Person person : people) {
    classUnderTest.handleStockOptions(person, callback);
  }
}

In the Listing 2 example, I provide implementations of two interfaces that use anonymous inner classes. First are two separate implementations of StockOptionEligible— one for Employees and one for Persons (to obey the interface). Then comes an implementation of StockOptionProcessingCallback that’s used to handle processing of stock options for the Manager instances.

It might take time to grasp the concept of anonymous inner classes, but they’re super handy. I use them all the time in my Java code. And as you progress as a Java developer, I believe you will too.

Test your understanding

  1. What is a nested class?

    1. A class that provides some utility to other classes in the application
    2. A class that is defined within another class
    3. A class where one or more interface references are passed into its constructor
    4. None of the above
  2. Why might you use a nested class?

    1. When you need to define a class that is tightly coupled (functionally) with another class
    2. When you want to write a class that makes use of a large number of interfaces from the JDK
    3. When one class needs to access to another class private data, but you have exceeded the maximum number of allowable classes for your application
    4. For a class has more than 20 methods defined on it and should be refactored
  3. Can you spot the error in the following code? Choose the best answer, explain your choice, and provide the correct code.

    
    package com.makotojava.intro.quiz;
    
    import java.util.logging.Logger;
    
    public class Outer {
     
     private static final Logger log = Logger.getLogger(Outer.class.getName());
     
     public void setInner(Inner inner) {
     this.inner = inner;
     }
    
     private Inner inner;
     
     public Inner getInner() {
    
     return inner;
     }
    
     private class Inner {
     }
     
     public static void main(String[] args) {
     Outer outer = new Outer();
     Inner inner = new Outer.Inner();
     outer.setInner(inner);
     log.info("Outer/Inner: " + outer.hashCode() + "/" + outer.getInner().hashCode());
     }
    
    }
    

    1. The class name Outer is not a legal Java class name.
    2. The class name Inner is confusing.
    3. The main method defined in class Outer has the wrong method signature.
    4. The log.info() call in main() has too many parameters.
    5. The class body for class Inner is empty.
    6. None of the above.
  4. Refer to the code listing in Question 3. Suppose you want a new class called AnotherOuter in the same package as Outer that could instantiate Outer.Inner. What change(s) would you need to make to the declaration of Outer.Inner?

    1. No changes are necessary; Inner can be instantiated as-is.
    2. No inner class can be instantiated outside its enclosing class.
    3. Change the visibility of Inner to protected or package-private.
    4. Add the static modifier to the Inner class declaration.
    5. None of the above.
  5. Given the following skeleton, flesh out the main() method to:

    1. Instantiate the Hello class
    2. Call Hellos talk() method, passing it an instance of HelloCallback implemented as an anonymous inner class
    3. Modify the anonymous inner class implementation of sayHello()to use the Logger defined on Hello to output: “This implementation says: ” The whatToSay string

      
      import java.util.logging.Logger;
      
      public class Hello {
         
         private static final Logger log = Logger.getLogger(Hello.class.getName());
         
         interface HelloCallback {
            void sayHello(String whatToSay);
         }
         
         public void talk(HelloCallback helloCallback) {
            helloCallback.sayHello("Hello, world (how original :/)...");
         }
      
         public static void main(String[] args) {
              // YOUR ANSWER GOES HERE
         }
      
      }
      

  6. Is it possible to write an inner class that can be instantiated by any class in your application (regardless of what package in which it resides) without an enclosing instance of the outer class? Explain your answer.

  7. What’s the difference between a nested class an an inner class?

    1. An inner class is one that is defined using private access only, whereas a nested class is declared static.
    2. The two terms are synonymous and can be used interchangeably.
    3. A nested class must reside inside the enclosing class and be declared before any of the enclosing class’s variables.
    4. A nested class is defined in main(), whereas an inner class can be defined anywhere.
    5. There is no such thing as a nested class.
    6. None of the above.
  8. Which of these statements is true regarding inner classes?

    1. An inner class can access any private data variables of its enclosing class unless it is declared static.
    2. An inner class must be declared public to be instantiated by any other class than its enclosing class.
    3. A static inner class is not allowed except under special circumstances.
    4. An inner class is completely invisible to its enclosing class.
    5. None of the above.

Check your answers

  1. What is a nested class?

    1. A class that provides some utility to other classes in the application
    2. A class that is defined within another class
    3. A class where one or more interface references are passed into its constructor
    4. None of the above
  2. Why might you use a nested class?

    1. When you need to define a class that is tightly coupled (functionally) with another class
    2. When you want to write a class that makes use of a large number of interfaces from the JDK
    3. When one class needs to access to another class private data, but you have exceeded the maximum number of allowable classes for your application
    4. For a class has more than 20 methods defined on it and should be refactored
  3. Can you spot the error in the following code? Choose the best answer, explain your choice, and provide the correct code.

    
    package com.makotojava.intro.quiz;
    
    import java.util.logging.Logger;
    
    public class Outer {
     
     private static final Logger log = Logger.getLogger(Outer.class.getName());
     
     public void setInner(Inner inner) {
     this.inner = inner;
     }
    
     private Inner inner;
     
     public Inner getInner() {
    
     return inner;
     }
    
     private class Inner {
     }
     
     public static void main(String[] args) {
     Outer outer = new Outer();
     Inner inner = new Outer.Inner();
     outer.setInner(inner);
     log.info("Outer/Inner: " + outer.hashCode() + "/" + outer.getInner().hashCode());
     }
    
    }
    

    1. The class name Outer is not a legal Java class name.
    2. The class name Inner is confusing.
    3. The main method defined in class Outer has the wrong method signature.
    4. The log.info() call in main() has too many parameters.
    5. The class body for class Inner is empty.
    6. None of the above. The line Inner inner = new Outer.Inner(); is in error. This is not the correct syntax for instantiating a nested class using an enclosing class reference. Rather, it should be: Inner inner = outer.new Inner();
  4. Refer to the code listing in Question 3. Suppose you want a new class called AnotherOuter in the same package as Outer that could instantiate Outer.Inner. What change(s) would you need to make to the declaration of Outer.Inner?

    1. No changes are necessary; Inner can be instantiated as-is.
    2. No inner class can be instantiated outside its enclosing class.
    3. Change the visibility of Inner to protected or package-private.
    4. Add the static modifier to the Inner class declaration.
    5. None of the above.
  5. Given the following skeleton, flesh out the main() method to:

    1. Instantiate the Hello class
    2. Call Hellos talk() method, passing it an instance of HelloCallback implemented as an anonymous inner class
    3. Modify the anonymous inner class implementation of sayHello()to use the Logger defined on Hello to output: “This implementation says: ” The whatToSay string

      
      import java.util.logging.Logger;
      
      public class Hello {
         
         private static final Logger log = Logger.getLogger(Hello.class.getName());
         
         interface HelloCallback {
            void sayHello(String whatToSay);
         }
         
         public void talk(HelloCallback helloCallback) {
            helloCallback.sayHello("Hello, world (how original :/)...");
         }
      
         public static void main(String[] args) {
              // YOUR ANSWER GOES HERE
         }
      
      }
      

      import java.util.logging.Logger;
      
      public class Hello {
      
      private static final Logger log = Logger.getLogger(Hello.class.getName());
      
      interface HelloCallback {
         void sayHello(String whatToSay);
      }
      
      public void talk(HelloCallback helloCallback) {
         helloCallback.sayHello("Hello, world (how original :/)...");
      }
      
      public static void main(String[] args) {
         Hello hello = new Hello();
      
         hello.talk(new HelloCallback() {
      
            @Override
            public void sayHello(String whatToSay) {
               log.info("This implementation says: " + whatToSay);
            }
      
         });
      }
      
      }
      
  6. Is it possible to write an inner class that can be instantiated by any class in your application (regardless of what package in which it resides) without an enclosing instance of the outer class? Explain your answer. Yes, it is. The inner class should be declared with the static modifier, along with public visibility. Then any class in your application can instantiate the inner class without an enclosing outer instance of the class.

  7. What’s the difference between a nested class an an inner class?

    1. An inner class is one that is defined using private access only, whereas a nested class is declared static.
    2. The two terms are synonymous and can be used interchangeably.
    3. A nested class must reside inside the enclosing class and be declared before any of the enclosing class’s variables.
    4. A nested class is defined in main(), whereas an inner class can be defined anywhere.
    5. There is no such thing as a nested class.
    6. None of the above.
  8. Which of these statements is true regarding inner classes?

    1. An inner class can access any private data variables of its enclosing class unless it is declared static.
    2. An inner class must be declared public to be instantiated by any other class than its enclosing class.
    3. A static inner class is not allowed except under special circumstances.
    4. An inner class is completely invisible to its enclosing class.
    5. None of the above.

Previous: InterfacesNext: Regular expressions