Nested and Inner Classes

COMP 310    Java Resources  Eclipse Resources

Java allows you to define classes inside of other classes.   Because of Java's lexical scoping rules (scoping is defined statically, by the language's syntax, e.g. by the nested curly braces in Java), classes defined as such garner some unique and powerful capabilities. 

Nested Classes

A nested class is simply a class definition that is marked "static" inside of another, "outer" class definition.    Being marked static means that the nested class definition exists at the class level of the outer class.   Since they are declared static, nested classes cannot be defined inside of a method.

A nested class is scoped to only to the static members of the outer class.

A nested class is referenced as a member of the outer class, e.g. MyOuterClass.MyNestedClass.

Nested classes are typically used simply to organize classes and avoid name conflicts.

Inner Classes

Inner classes declartions are not marked static and thus exist at the instance level of a the outer class.  There are two types of inner classes:  Named Inner Classes and Anonymous Inner Classes.

Since inner classes are not static, they can be defined inside of a method.   Because of this, inner classes are scoped to anything that would normally be in scope at that level of curly-brace indention.  

Scoping restriction:  In order for an inner class to access a local variable, that variable MUST be declared final or be "effectively final", meaning that the variable itself cannot ever be reassigned, i.e. mutated.   If the inner class references a non-final local variable that is mutated anywhere in the code, a compiler error will be thrown. This is because the compiler copies the value of the local value from the "stack" where local variables live (and die) to the "heap" where persistent field-level variables exist.    This means that local variables accessed by an inner class must be immutable.    Note that a reference to an object (a typical variable referencing an object) can be immutable but the referenced object can still be mutable.     There are tricks around this issue however for primitives as well.

It is recommended that even though the compiler may not demand it, since an inner class- referenced local variable must be immutable, it is best to declare it as final so that any attempt to mutate it will be immediately flagged by the compiler for what the problem actually is, namely a mutating a variable that is supposed to be immutable rather than the compiler flagging the secondary issue of referencing a non-final variable.

Named Inner Classes

Named inner classes look just like nested classes but with not static declaration.   They can be declared anywhere and can be used just like any other class anywhere for which they are scoped.   Note that a nested inner class is able to access variables that are in scope where the nested class is defined, not the variables where it is used to instantiate an object.

Named inner classes are useful when you need to instantiate more than one instance of  that class.

Anonymous Inner Classes

Anonymous inner classes are class definitions that have no name.     An anonymous inner class declaration both defines the class and instantiates an instance of that class.    Thus the result of an anonymous inner class declaration is a reference to an object.

Anonymous inner class definitions are very useful when only a single instance of the class is needed. 

Technically, anything that can be done with an anonymous inner class can also be done using a named inner class, albeit more tediously.

Syntax:

//  param1, param2, etc are the input parameters to the MySuperClass constructor
new MySuperClass(param1, param2, etc) {
    // field declarations.   Visibility declaration is moot because the class has no name
    FieldType1  f1 = initialValue1;
    FieldType2 f2 = initialValue2;

    {
        // initializer block.   Executes once at instantiation time.
        // serves as a substitute constructor, though the superclass
        // constructor has already been called.
    }
    
    /**
    * method declaration.   If overriding a method, visibility and rest of signature must match
    * otherwise visibility is moot.
    **/
    visibility ReturnType1  method1(InputType p1, etc) {
        // code here
    } 
}

The above code creates an instance of a subclass of MySuperClass.    Since that that subclass has no name, the instantiated object can only be referenced as an instance of MySuperClass.

Click here for more advanced inner class techniques.

 

 

 

© 2020 by Stephen Wong