Java 1.1 introduced changes to the Java Language Specification that greatly simplified implementation of some constructs commonly found in practice. Among those changes are inner and anonymous classes. If used properly, they can make your program easier to understand and maintain. Let's look at how these features work, how to properly use them, and how to avoid some common errors.
Put in simple terms, an inner class is a class declared inside another class. Starting with Java 1.1, you can declare a class inside another, much like you declare fields and methods. The class that encloses the declaration of the inner class is called the outer class.
Actually, the language specification allows much more than that. You can, for example:
- Declare a class inside another class or inside an interface.
- Declare an interface inside another interface or inside a class.
- Declare a class inside a method.
- Nest classes and interfaces declarations arbitrarily deep.
Listing A presents empty declarations of classes and the interfaces showing these possibilities.
Using an import statement, you can omit the package name just as you do for any other regular class. Moreover, inside the outer class, you can refer to all inner classes and interfaces by their simple names (see the new statements in Listing A). Notice that referring to Inner2 from Method1 still requires specifying Interface1, since Inner2 is at a different level.
Table A shows the fully qualified name for each inner class and interface declared in Listing A. By using import statements, it's possible to use a shorter form. Inside the outer class, of course, you can also omit the outer class’s name.
Inner3is local toMethod1, so it
can’t be accessed outside the method
The natural application for inner classes is to declare classes that are used only inside some other class, or that are closely related to another. Consider Listing B, which is a very simple implementation of a linked list. Because there isn't much reason to use the class Node without the realm of LinkedList, it makes sense to declare the later an inner class of the former.
The same access control modifiers that apply to class members also apply to inner classes—that is, inner classes can have package, protected, private, and public access, and the semantics are the same. Since Node is meant to be used outside LinkedList, it is declared public.
The modifier static has a different meaning, however. When applied to inner classes, it declares the class to be semantically the same as any other class, meaning that it can be instantiated and used like a regular class. The only difference is that it has full access to all static members of the outer class. Listing C presents a simple program that creates a linked list and prints it to the standard output.
Nonstatic inner classes
Inner classes that don’t specify the static modifier have full access to all members of the outer class, including instance fields and methods. To make that possible, nonstatic inner classes store an implicit reference to an instance of the outer class.
Because of that, instantiating a nonstatic inner class requires a bit different syntax of the new statement:
<outer class object>.new <inner class name>
This form of the new statement requires an instance of the outer class so that the inner class can be created in the context of that instance. Notice that Listing A declared several nonstatic inner classes and instantiated them in Method1 using a regular new statement.
That is possible because Method1 is an instance method of the outer class, so the new statement is implicitly executed inside the context of an instance of the outer class. The modified syntax is necessary only when instantiating a nonstatic inner class outside the outer class or inside the context of an object other than this.
Nonstatic inner classes have some limitations, though. Specifically, they cannot declare static initializers and static members, except in constant fields. Besides that, inner classes declared inside methods cannot access the method’s local variables and parameters, unless they are declared final.
Anonymous classes are classes that don’t have a name (hence the term anonymous). Because they don’t have a name, there is no way to refer to them. For this reason, their declaration must be given at creation time, as part of the new statement.
That requires yet another form of the new statement, as follows:
new <class or interface> <class’ body>
This form of the new statement declares a new anonymous class that extends a given class or implements a given interface. It also creates a new instance of that class and returns it as the result of the statement. The class to extend or the interface to implement is the operand of the new statement, followed by the anonymous class’ body.
If the anonymous class extends another class, its body can access the class’s members, override its methods, and so on, like any other regular class. If the anonymous class implements an interface, its body must implement the interface’s methods.
Notice that the declaration of the anonymous class is made at compile time, and the instantiation at runtime. That means that a new statement inside a for loop, for example, creates several instances of the same anonymous class, and not one instance of several different anonymous classes.
Technically, anonymous classes are considered nonstatic inner classes, so they have the same privileges and limitations of nonstatic inner classes declared inside a method.
Anonymous classes are great when you need to perform some task that needs an object, but that doesn’t justify creating a whole new class, maybe because the needed class is too simple or because it's used only inside a single method. Anonymous classes are particularly useful for quickly creating event handlers in Swing applications.
Listing D is a very simple Swing application that shows several of the concepts related to anonymous classes. The example creates two anonymous classes. The first extends java.awt.event.WindowAdapter and calls the application onClose method when the application’s window is closed.
The anonymous class can call onClose even though it is being declared private because the anonymous class is an inner class of the application class. The second anonymous class implements the java.awt.ActionListener interface and closes the application’s window when a button is pressed. Notice that the anonymous class has access to the local variable frame. This is possible because the anonymous class is declared inside the same method as frame. The frame variable was declared final, though. Otherwise, a compilation error would be generated.
Inner and anonymous classes are great tools introduced in Java 1.1. They provide better encapsulation and, consequently, make the code easier to understand and maintain, keeping related classes in the same source file (i.e., inner classes) and avoiding proliferation of very small classes (i.e., anonymous classes).