| Contents | Prev | Next | Inner Classes Specification | 
Here are some of the properties that make inner classes useful:
In addition, the programmer can define a class as a static member of any 
top-level class.  Classes which are static class members and classes which 
are package members are both called top-level classes.  They differ from inner 
classes in that a top-level class can make direct use only of its own instance 
variables.  The ability to nest classes in this way allows any top-level class to 
provide a package-like organization for a logically related group of secondary 
top-level classes, all of which share full access to private members.
Inner classes and nested top-level classes are implemented by the compiler, and do not require any changes to the Java Virtual Machine. They do not break source or binary compatibility with existing Java programs.
All of the new nested class constructs are specified via transformations to Java 1.0 code that does not use inner classes. When a Java 1.1 compiler is producing Java virtual machine bytecodes, these bytecodes must represent the results of this (hypothetical) source-to-source transformation, so that binaries produced by different Java 1.1 compilers will be compatible. The bytecodes must also be tagged with certain attributes to indicate the presence of any nested classes to other Java 1.1 compilers. This is discussed further below.
Here is an incomplete class FixedStack which implements a stack, and is 
willing to enumerate the elements of the stack, from the top down: 
The interface java.util.Enumeration is used to communicate a series of 
values to a client.  Since FixedStack does not (and should not!) directly 
implement the Enumeration interface, a separate adapter class is required to 
present the series of elements, in the form of an Enumeration.  Of  course, the 
adapter class will need some sort of access to the stack's array of elements.  If 
the programmer puts the definition of the adapter class inside of FixedStack, 
the adapter's code can directly refer to the stack object's instance variables.
In Java, a class's non-static members are able to refer to each other, and they 
all take their meaning relative to the current instance this.  Thus, the instance 
variable array of FixedStack is available to the instance method push and 
to the entire body of the inner class FixedStack.Enumerator.  Just as 
instance method bodies "know" their current instance this, the code within 
any inner class like Enumerator "knows" its enclosing instance, the instance of 
the enclosing class from which variables like array are fetched.
One of the ways in which the FixedStack example is incomplete is that there 
is a race condition among the operations of the FixedStack and its 
Enumerator.  If a sequence of pushes and pops occurs between calls to 
nextElement, the value returned might not be properly related to previously 
enumerated values; it might even be a "garbage value" from beyond the 
current end of the stack.  It is the responsibility of the programmer to defend 
against such race conditions, or to document usage limitations for the class.  
This point is discussed later.  One defense against races looks like this: 
The expression FixedStack.this refers to the enclosing instance.
For the moment, we say nothing about how this code works, but Java's rules of 
scoping and variable semantics precisely require what this code does.  Even 
after the method myEnumerate returns, array can still be used by the inner 
object; it does not "go away" as in C.  Instead, its value continues to be 
available wherever that value is required, including the two methods of E.
Note the final declaration.  Local final variables such as array are a new 
feature in 1.1.  In fact, if a local variable or parameter in one class is referred to 
by another (inner) class, it must be declared final.  Because of potential 
synchronization problems, there is by design no way for two objects to share 
access to a changeable local variable.  The state variable count could not be 
coded as a local variable, unless perhaps it were changed a one-element array: 
(Sometimes the combination of inheritance and lexical scoping can be 
confusing.  For example, if the class E inherited a field named array from 
Enumeration, the field would hide the parameter of the same name in the 
enclosing scope.  To prevent ambiguity in such cases, Java 1.1 allows inherited 
names to hide ones defined in enclosing block or class scopes, but prohibits 
them from being used without explicit qualification.)
E adds little or no clarity to the 
code.  The problem is not that it is too short:  A longer name would convey 
little additional information to the maintainer, beyond what can be seen at a 
glance in the class body.  In order to make very small adapter classes as concise 
as possible, Java 1.1 allows an abbreviated notation for local objects.  A single 
expression syntax combines the definition of an anonymous class with the 
allocation of the instance: 
In general, a new expression (an instance creation expression) can end with a 
class body.  The effect of this is to take the class (or interface) named after the 
new token, and subclass it (or implement it) with the given body.  The resulting 
anonymous inner class has the same meaning as if the programmer had 
defined it locally, with a name, in the current block of statements.
Anonymous constructs like these must be kept simple, to avoid deeply nested code. When properly used, they are more understandable and maintainable than the alternatives-named local classes or top-level adapter classes.
If an anonymous class contains more than a line or two of executable code, then its meaning is probably not self-evident, and so a descriptive local name should be given to either the class or (via a local variable) to the instance.
An anonymous class can have initializers but cannot have a constructor.  The 
argument list of the associated new expression (often empty) is implicitly 
passed to a constructor of the superclass.
As already hinted, if an anonymous class is derived from an interface I, the 
actual superclass is Object, and the class implements I rather than extending 
it.  (Explicit implements clauses are illegal.)  This is the only way an interface 
name can legally follow the keyword new.  In such cases, the argument list 
must always be null, to match the constructor of the actual superclass, Object.
Inner Classes Specification (HTML generated by dkramer on March 15, 1997)
Copyright © 1996, 1997 Sun Microsystems, Inc.
All rights reserved
Please send any comments or corrections to john.rose@eng.sun.com