http://www.ibm.com/developerworks/java/tutorials/j-introtojava2/section7.html
Nested classes
In this section, learn about nested classes and where and how to use them.
As its name suggests, a nested class is one defined within another class. Here is a nested class:
public class EnclosingClass { . . . public class NestedClass { . . . } } |
Just like member variables and methods, Java classes can also be defined at any scope including public
, private
, orprotected
. Nested classes can be useful when you want to handle internal processing within your class in an object-oriented fashion, but this functionality is limited to the class where you need it.
Typically, you'll use a nested class for cases where you need a class that is tightly coupled with the class in which it is defined. A nested class has access to the private data within its enclosing class, but this carries with it some side-effects that are not obvious when you start working with nested (or inner) classes.
Because a nested class has scope, it is 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 Employee
s 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
will differ from one Manager
to another. In this case, it makes sense that one would only reference the DirectReports
nested class in the context of its enclosing instance of Manager
, so I've made itprivate
.
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 17:
public class Manager extends Employee { public Manager() { } . . . private class DirectReports { . . . } } // public static void main(String[] args) { Manager.DirectReports dr = new Manager.DirectReports();// This won't work! } |
The code in Listing 17 doesn't work, and you're probably wondering why. The problem (and also its solution) lies with the wayDirectReports
is defined within Manager
, and with the rules of scope.
If you had a member variable of Manager
, you would 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 you defined it in Listing 17.
To create an instance of a public nested class, you use a special version of the new
operator. Combined with a reference to some enclosing instance of an outer class, new
allows you to create an instance of the nested class:
public class Manager extends Employee { public Manager() { } . . . private class DirectReports { . . . } } // Meanwhile, in another method somewhere... public static void main(String[] args) { Manager manager = new Manager(); Manager.DirectReports dr = manager.new DirectReports(); } |
Note 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.
At times you will want to create a class that is 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 of this 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 they should really only be used when you need to couple a class tightly with its definition. Clearly, in the case of a utility class likeManagerComparator
, 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.
The Java language allows you to declare classes pretty much anywhere, even in the middle of a method if necessary, and even without providing a name for the class. This is basically a compiler trick, but there are times when anonymous inner classes are extremely handy to have.
Listing 18 builds on the example in Listing 15, adding a default method for handling Employee
types that are notStockOptionEligible
:
Employee
types that are not StockOptionEligible
public static void main(String[] args) { Employee employee = new Manager();// perfectly valid handleStockOptions(employee); employee = new Employee();// not StockOptionEligible handleStockOptions(employee); } . . . private static void handleStockOptions(Employee e) { if (e instanceof StockOptionEligible) { calculateAndAwardStockOptions((StockOptionEligible)e); } else { calculateAndAwardStockOptions(new StockOptionEligible() { @Override public void awardStockOptions(int number, BigDecimal price) { log.info("Sorry, you're not StockOptionEligible!"); } }); } } . . . private static void calculateAndAwardStockOptions(StockOptionEligible soe) { BigDecimal reallyCheapPrice = BigDecimal.valueOf(0.01); int numberOfOptions = 10000; soe.processStockOptions(numberOfOptions, reallyCheapPrice); } |
In this example, you provide an implementation of the StockOptionEligible
interface by using an anonymous inner class for instances of Employee
that do not implement that interface. Anonymous inner classes are also useful for implementing callback methods, and in event handling, too.