Contents | Prev | Next Inner Classes Specification


How do inner classes work?

Inner class code is typically defined relative to some enclosing class instance, so the inner class instance needs to be able to determine the enclosing instance.

The JavaSoft Java 1.1 compiler arranges this by adding an extra private instance variable which links the inner class to the enclosing class. This variable is initialized from an extra argument passed to the inner class constructor. That argument, in turn, is determined by the expression which creates the inner class instance; by default it is the object doing the creation.

The Java 1.1 Language Specification specifies that the name of a type which is a class member, when transformed into Java 1.0 code for the purpose of generating Java virtual machine bytecodes, consists of the fully qualified name of the inner class, except that each `.' character following a class name is replaced by a `$'. In addition, each inner class constructor receives the enclosing instance in a prepended argument. Here is how the transformed source code of the FixedStack example might look:


public class FixedStack { ... (the methods omitted here are unchanged) public java.util.Enumeration elements() { return new FixedStack$Enumerator(this); } } class FixedStack$Enumerator implements java.util.Enumeration { private FixedStack this$0; // saved copy of FixedStack.this FixedStack$Enumerator(FixedStack this$0) { this.this$0 = this$0; this.count = this$0.top; } int count; public boolean hasMoreElements() { return count > 0; } public Object nextElement() { if (count == 0) throw new NoSuchElementException("FixedStack"); return this$0.array[--count]; } }

Anyone who has already programmed with Java or C++ adapter classes has written code similar to this, except that the link variables must be manually defined and explicitly initialized in top-level adapter classes, whereas the Java 1.1 compiler creates them automatically for inner classes.

When the Enumerator needs to refer to the top or array fields of the enclosing instance, it indirects through a private link called this$0. The spelling of this name is a mandatory part of the transformation of inner classes to the Java 1.0 language, so that debuggers and similar tools can recognize such links easily. (Most programmers are happily unaware of such names.)

(Note: There is a limitation in some implementations of Java 1.1, under which the initialization of this$0 is delayed until after any superclass constructor is run. This means that up-level references made by a subclass method may fail if the method happens to be executed by the superclass constructor.)

References to local variables

A class definition which is local to a block may access local variables. This complicates the compiler's job. Here is the previous example of a local class:

Enumeration myEnumerate(final Object array[]) { class E implements Enumeration { int count = 0; public boolean hasMoreElements() { return count < array.length; } public Object nextElement() { { return array[count++]; } } return new E(); }

In order to make a local variable visible to a method of the inner class, the compiler must copy the variable's value into a place where the inner class can access it. References to the same variable may use different code sequences in different places, as long as the same value is produced everywhere, so that the name consistently appears to refer to the same variable in all parts of its scope.

By convention, a local variable like array is copied into a private field val$array of the inner class. (Because array is final, such copies never contain inconsistent values.) Each copied value is passed to the inner class constructor as a separate argument of the same name.

Here is what the resulting transformed code looks like:


Enumeration myEnumerate(final Object array[]) { return new MyOuterClass$19(array); } ... class MyOuterClass$19 implements Enumeration { private Object val$array[]; int count; MyOuterClass$19(Object val$array[]) { this.val$array = val$array; count = 0; } public boolean hasMoreElements() { return count < val$array.length; } public Object nextElement() { return val$array[count++]; } }

A compiler may avoid allocating an inner class field to a variable, if it can determine that the variable is used only within the inner class constructors.

Notice that a class defined by a block, like E, is not a member of its enclosing class, and so it cannot be named outside of its block. This is the same scoping restriction as applies to local variables, which also cannot be named outside of their blocks. In fact, any class contained in a block (whether directly or inside an intervening local class) cannot be named outside the block. All such classes are called inaccessible. For purposes of linking, the compiler must generate a unique externally visible name for every inaccessible class. The overall form of these names is a class name, followed by additional numbers or names, separated by $ characters.

Also, variable names synthesized by the compiler beginning with this$ and val$ must follow the usage patterns described here.

These names and conventions must be recognized by 1.1-compliant tools, and are strongly suggested for most compilation purposes. They are discussed further in the section on binary compatibility.

It must be emphasized that these oddly-named "this$" and "val$" fields and extra constructor arguments are added by the compiler to the generated bytecodes, and cannot be directly referenced by Java source code. Likewise, bytecode-level class names like MyOuterClass$19 cannot be used by source code (except under pre-1.1 compilers, which know nothing of inner classes).


Contents | Prev | Next

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