SDSU CS 596 Java Programming
Fall Semester, 1998
Nested & Inner Classes
To Lecture Notes Index
© 1998, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 13-Nov-98

Contents of Doc 17, Nested & Inner Classes


References


The Java Programming Language , 2 nd Ed., Arnold & Gosling, Chapter 2

Java in a Nutshell , 2 nd Ed., David Flanagan, O'Reilly, 1997, Chapter 5.

Listen Here!S-oct28 5mins Doc 17, Nested & Inner Classes Slide # 2

Nested Classes


Java 1.1 introduced nested classes

On first reading you may find that nested classes and inner classes have some seemingly arbitrary restrictions. The restrictions occur because nested classes were not part of the original language design. They did not want to change the JVM to incorporate nested/inner classes. As a result the compiler has to use Java 1.0 features to implement nested/inner classes. In order to do this some restrictions are placed on nested/inner classes.
The need for some types of nested/inner class may not be apparent at this time. However, they were added they were needed. The event model for GUI components introduced in Java 1.1 is a major motivation for inner classes. Also inner classes are used with inheritance and interfaces. When we cover interfaces and Java's GUI components inner classes will make much more sense.

Static nested classes
Called top-level nested classes (an oxymoron)

A nested class that is not static is called an inner class

There are three types of inner classes:

Member Classes
Local Classes
Anonymous Classes


Listen Here!S-oct28 6mins, Q-nov5 4mins Doc 17, Nested & Inner Classes Slide # 3

Top-Level Nested Classes


A class defined inside another class (but not in any other block) and declared static is a top-level nested class

public class OutSide {
   private String name = "Roger";
   
   public static class Nested {
      private int size;
      
      public Nested( int size ) {
         this.size = size;
      }
      
      public void print() {
         System.out.println( "Nested " + size );
      }
   }
   
   public void print() {
         System.out.println( "Outside " + name );
         (new Nested( 10 )).print();
      }
}
public class TestNested {
   public static void main( String[] arguments) {
      OutSide pitch = new OutSide();
      pitch.print();
      OutSide.Nested a = new OutSide.Nested( 5 );
      a.print();
   }
}
Output
Outside Roger
Nested 10
Nested 5

Listen Here!S-oct28 2mins Doc 17, Nested & Inner Classes Slide # 4
Difference Between Ordinary Classes

Nested top-level classes differ from ordinary classes by:


Access levels

Ordinary classes have two access levels:
public
package

Nested top-level classes have four access levels:
public
protected
private
package

Note the Java interpreter only recognizes only public and package level access for all class. The additional access levels for nested top-level classes are enforced by the compiler. This means that you could write Java byte code to violate the protected and private access levels of a nested top-level class. This was done to avoid having to change the Java interpreter.

Name

The name of a nested top-level class includes the name of the enclosing class

Listen Here!S-oct28 46secs Doc 17, Nested & Inner Classes Slide # 5
Using Import & Packages with Static Nested Classes
Package with Nested Class
package NameExample;
public class OutSide {
   
   public static class Nested {
   }
}
Import the outer class, qualify nested class

import NameExample.OutSide;
public class NameTest {
   public static void main( String[] args ) {
      OutSide a = new OutSide();
      OutSide.Nested b = new OutSide.Nested( 3 );
   }
}
The wild card does not allow you to shorten the name of the nested class

import NameExample.*;
public class Test {
   public static void main( String[] args ) {
      OutSide a = new OutSide();
      OutSide.Nested b = new OutSide.Nested( );
   }
}


Listen Here!S-oct28 3mins Doc 17, Nested & Inner Classes Slide # 6
Using the import to shorten the nested class name

import NameExample.OutSide;
import NameExample.OutSide.Nested;
public class NameTest {
   public static void main( String[] args ) {
      OutSide a = new OutSide();
      Nested b = new Nested( 3 );
   }
}

Using full class names

public class NameTest {
   public static void main( String[] args ) {
      NameExample.OutSide a = new NameExample.OutSide();
      NameExample.OutSide.Nested b = 
         new NameExample.OutSide.Nested(  );
   }
}

Listen Here!S-oct28 51secs Doc 17, Nested & Inner Classes Slide # 7

Inner Classes


Nested top-level classes are nested for organizational convenience. The nesting has no additional semantics than the name and access level of the class. With inner classes the nesting serves an additional purpose. All objects created from an inner class must be enclosed in an object created from the enclosing class. Examples will make this clear.

Member Classes


A non-static class nested in another class

Each instance has an enclosing instance and can access its enclosing instances fields and methods.

Can not have static members

Can not have the same name as the containing class

On the next slide there is an example of a member class, "Counter". "Counter" is declared inside the class "DataStuff", but "Counter" is not a static class. Note that "Counter" can access the private field of "DataStuff".

The class "Test" shows the two ways to create a Counter object. The first is from inside the class "DataStuff". This is done in the method getCounter(). The statement " a.getCounter();" creates a counter object that is enclosed (or connected) to the DataStuff object referenced by "a". The compiler does this enclosing (or connecting) by inserting a private field in the class "Counter" which references the proper DataStuff object. The statement "counterA.increase();" uses this special field to access the field "size" in the object referenced by "a".

The second way to create a Counter object is from outside the class "DataStuff". The statement "b.new Counter();" shows the syntax of doing this. Note the new syntax for new. It is preceded with an object reference and a ".". In "b.new Counter();" object b becomes the enclosing object of the new Counter.

Listen Here!S-oct28 15mins, Q-nov5 13mins Doc 17, Nested & Inner Classes Slide # 8
Member Class Example

public class DataStuff {
   private int size = 0;
   
   public class Counter {
      
      public void increase(  ) {
         size++;      //accessing enclosing classes private field
      }
   }// class Counter
   
   public String toString() {
      return String.valueOf( size);      
   }
   
   public Counter getCounter() {
      return new Counter();
   }
}//class DataStuff
public class Test{
   public static void main( String args[] ) {
      DataStuff a = new DataStuff();
      DataStuff b = new DataStuff();
      DataStuff.Counter counterA = a.getCounter();
      DataStuff.Counter counterB = b.new Counter();
      counterA.increase();
      counterA.increase();
      counterB.increase();
      System.out.println( "A " + a + " B " + b );
      }
   }
Output
A 2 B 1

Listen Here!S-oct28 2mins Doc 17, Nested & Inner Classes Slide # 9
Member Classes & Access Levels

Member Classes can have the access levels:
public
protected
private
package

The interpreter only knows about public and package level access for classes. The compiler is the one that enforces the other access levels. This means one could modify the Java byte code to violate the access levels of member classes.

Member Classes & Import

You can use the import statement to use a short version of a member class outside the enclosing class

import DataStuff.Counter;
public class Test {
   public static void main( String args[] )  {
      DataStuff a = new DataStuff();
      DataStuff b = new DataStuff();
      Counter counterA = a.getCounter();
      Counter counterB = b.new Counter();
      counterA.increase();
      counterA.increase();
      counterB.increase();
      System.out.println( "A " + a + " B " + b );
   }
}

Listen Here!S-oct28 4mins Doc 17, Nested & Inner Classes Slide # 10
Member Classes & this

"this" in a member class refers to the current object. It can be used to refer to fields and methods of the current object. "this" can not be used to refer to fields and method of the enclosing class.

public class DataStuff {
   private int size = 0;
   
   public class Counter {
      public void increase(  ) {
         this.size++;         // Compile error
      }
   }// class Counter
}
To refer to the enclosing class use ClassName.this

public class DataStuff {
   private int size = 0;
   
   public class Counter {
      
      public void increase(  ) {
         DataStuff.this.size++;
      }
   }// class Counter
}

Listen Here!S-oct28 5mins, Q-nov5 2mins Doc 17, Nested & Inner Classes Slide # 11
More this & Member Class

Another example of using the className.this to access enclosing class fields

public class A {
   String name = "A";
   
   public class B {
      String name = "B"; 
      
      public class C {
         String name = "C";
         
         public void print( String name ) {
            System.out.println( name ); //prints the argument
            System.out.println( this.name );      // C
            System.out.println( C.this.name );   // C
            System.out.println( B.this.name );   // B
            System.out.println( A.this.name );   // A
         }
      }
   }
}

Listen Here!Q-nov5 32secs Doc 17, Nested & Inner Classes Slide # 12

Local Classes


A local class is a class declared in within a block of code

A local class is visible and usable within the block of code in which it is defined

A local class can use any final variable or method parameters that are visible from the scope in which it is defined

A local class cannot:

Use any variable or method parameter that are visible from the scope in which it is defined and is not final
Contain fields, methods, or classes that are declared static
Be declared public, protected, private, or static
Have the same name as any of its enclosing classes

The reason a local class can not access a non-final data in the enclosing class is due to the way local classes are implemented. Each object from a local class is given its own copies of all final data items in its scope. This was done to avoid changing the JVM & byte code for local classes. Given the way local classes are used, this restriction normally does not cause any problems. If you find that this restriction is a problem, your design may need to be reviewed. It is likely that you are misusing local classes.

Listen Here!S-oct28 6mins, Q-nov5 13mins Doc 17, Nested & Inner Classes Slide # 13
Local Class Example
This example shows the syntax of a local class. It also shows one of the problems with a local class: readability. How does one format the source code to make it readable? This example is a good example of how to misuse a local class. As with nested and all inner classes, local classes should be use infrequently.

public class TopLevel {
   private int size = 0;
   
   public int process( final int input ) {
      class Helper {
         int localSize;
         public Helper( int sizeIn ) {
            localSize = sizeIn;
         }
         public int useful( ) {
            return input + localSize;
         }
      
      }// Class Helper
      Helper myFriend = new Helper( size);
      return myFriend.useful();
   } // process
} // class TopLevel

Listen Here!S-oct28 6mins, Q-nov5 3mins Doc 17, Nested & Inner Classes Slide # 14

Anonymous Classes


An anonymous class is a local class with no name
Since an anonymous class has no name, you can not declare a variable or field to be of that type. For this reason, anonymous classes must either be a subclass of another class or implement an interface. Below is an example of an anonymous class implementing an interface. The basic syntax is:

new InterfaceName() { classDefinitionHere };

An anonymous class is always defined in a statement. In this example, it is part of a return statement. This means the semi-colon is needed after the "}" in the class definition to end the return statement.

public class SimpleVector {
   Object[] elements = new Object[100];
   int elementCount = 0;
   
   public void addElement( Object element )    {
      elements[elementCount++] = element;
   }
   public Object elementAt( int index ) { return elements[ index ]; }
   
   public int size() {  return elementCount; }
   
   public Enumeration elements() {
      return new Enumeration( ) {
         int nextElementIndex = 0;
      
         public boolean hasMoreElements() {
            return nextElementIndex < elementCount;
         }
         
         public Object nextElement() {
            return elements[ nextElementIndex++ ];
         }
      };  // ";" is needed to end the return statement
   }
}

Doc 17, Nested & Inner Classes Slide # 15
Anonymous Class as Subclass

In the example on the next slide, an anonymous class is declared a subclass. The basic syntax is:

new ParentClassName( parameters ) { classDefinitionHere };

The parameter list is used to match the constructor in the parent class. In the example, the class Foo has a constructor with one parameter. Thus, the anonymous class in Outer must have one argument. Since anonymous classes can not define their own type, it does not make any sense to define new methods in an anonymous class. Note there is no way to use the method notReachable below.


Listen Here!S-oct28 7mins, Q-nov5 3mins Doc 17, Nested & Inner Classes Slide # 16
Anonymous Class as Subclass Example

public class Foo {
   int data;
   
   public String toString() { return String.valueOf( data );  }
   
   public void add( int addValue ) { data = data + addValue; }
   
   public Foo( int initialValue ) {   data = initialValue; }
}
   
public class Outer {
   public void bar(int aValue ) {
      Foo what = new Foo( aValue ) {
         public void add( int addValue ) { data += addValue + 2; }
         public void notReachable() { data++; }
         };
      
      System.out.println( what );
      what.add( 1 );
      System.out.println( what );
   }
}
public class FooTest() {
   public static void main(String args[] ) {
      Outer test = new Outer();
      test.bar( 1 );
      test.bar( 10 );
   }      
}
Output
1
4
10
13

Listen Here!S-oct28 1min Doc 17, Nested & Inner Classes Slide # 17

Enumeration Examples of Nested/Inner Classes

I will use SimpleVector and SimpleVectorEnumeration from Doc 10, slide 12 to illustrate the different types of nested and inner classes.

No Nested Classes

The first example uses normal classes. SimpleVector is a public class, SimpleVectorEnumeration a package level class.

public class SimpleVector {
   Object[] elements = new Object[100];
   int elementCount = 0;
   
   public void addElement( Object element )    {
      elements[elementCount++] = element;
   }
   public Object elementAt( int index ) { return elements[ index ]; }
   
   public int size() {  return elementCount; }
   
   public Enumeration elements() {
      return new SimpleVectorEnumeration( this );
   }
}
class SimpleVectorEnumeration implements Enumeration {
   SimpleVector myVector;
   int nextElementIndex = 0;
   
   public SimpleVectorEnumeration( SimpleVector aVector ) {
      myVector = aVector;
   }
   
   public boolean hasMoreElements() {
      return nextElementIndex < myVector.size();
   }
   public Object nextElement() {
      return myVector.elementAt( nextElementIndex++ );
   }
}

Listen Here!S-oct28 2mins Doc 17, Nested & Inner Classes Slide # 18

Top-Level Nested Class


SimpleVectorEnumeration as a top-level nested class. We gain better protection, as SimpleVectorEnumeration can be made private. However, this is not a big gain. Who would know that package level SimpleVectorEnumeration in the last slide exists? We lose some readability. Where does SimpleVector end?

public class SimpleVector {
   Object[] elements = new Object[100];
   int elementCount = 0;
   
   public void addElement( Object element )    {
      elements[elementCount++] = element;
   }
   public Object elementAt( int index ) { return elements[ index ]; }
   
   public int size() {  return elementCount; }
   
   public Enumeration elements() {
      return new SimpleVectorEnumeration( this );
   }
   private static class SimpleVectorEnumeration 
                                    implements Enumeration {
      SimpleVector myVector;
      int nextElementIndex = 0;
      
      public SimpleVectorEnumeration( SimpleVector aVector ) {
         myVector = aVector;
      }
      
      public boolean hasMoreElements() {
         return nextElementIndex < myVector.size();
      }
      public Object nextElement() {
         return myVector.elementAt( nextElementIndex++ );
      }
   }
}

Listen Here!S-oct28 2mins Doc 17, Nested & Inner Classes Slide # 19

Member Class


As a member class, SimpleVectorEnumeration becomes simpler. It has direct access to SimpleVector's fields. This does violate the information hiding of the SimpleVector class. However, the two classes are already tightly coupled. While there seems to be no reason to make SimpleVectorEnumeration a top-level nested class, the implementation simplification as member class is attractive.

public class SimpleVector {
   Object[] elements = new Object[100];
   int elementCount = 0;
   
   public void addElement( Object element )    {
      elements[elementCount++] = element;
   }
   public Object elementAt( int index ) { return elements[ index ]; }
   
   public int size() {  return elementCount; }
   
   public Enumeration elements() {
      return new SimpleVectorEnumeration(  );
   }
   
   private class SimpleVectorEnumeration implements Enumeration {
      int nextElementIndex = 0;
      
      public boolean hasMoreElements() {
         return nextElementIndex < size();
      }
      public Object nextElement() {
         return elementAt( nextElementIndex++ );
      }
   }
}

Listen Here!S-oct28 2mins Doc 17, Nested & Inner Classes Slide # 20

Local Class


As a local class the SimpleVectorEnumeration can only be used in the elements() method. This may or may not be desirable. It does make the elements() method harder to read. The one statement in the method gets lost in the declaration of the local class.

public class SimpleVector {
   Object[] elements = new Object[100];
   int elementCount = 0;
   
   public void addElement( Object element )    {
      elements[elementCount++] = element;
   }
   public Object elementAt( int index ) { return elements[ index ]; }
   
   public int size() {  return elementCount; }
   
   public Enumeration elements() {
      class SimpleVectorEnumeration implements Enumeration {
         int nextElementIndex = 0;
         
         public boolean hasMoreElements() {
            return nextElementIndex < size();
         }
         public Object nextElement() {
            return elementAt( nextElementIndex++ );
         }
      }
      return new SimpleVectorEnumeration(  );
   }
}

Listen Here!S-oct28 2mins Doc 17, Nested & Inner Classes Slide # 21

Anonymous Class


As an anonymous class the SimpleVectorEnumeration can only be used in the elements() method. This may or may not be desirable. This seems to me to be much easier to read than the local class example, as it starts with the return. The last semi-colon seems out of place.

public class SimpleVector {
   Object[] elements = new Object[100];
   int elementCount = 0;
   
   public void addElement( Object element )    {
      elements[elementCount++] = element;
   }
   public Object elementAt( int index ) { return elements[ index ]; }
   
   public int size() {  return elementCount; }
   
   public Enumeration elements() {
      return new Enumeration( ) {
         int nextElementIndex = 0;
      
         public boolean hasMoreElements() {
            return nextElementIndex < elementCount;
         }
         
         public Object nextElement() {
            return elements[ nextElementIndex++ ];
         }
      };
   }
}

Copyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.

visitors since 20-Oct-98