SCJP Study Guide:
Declarations, Initialization and Scoping


Printer-friendly version Printer-friendly version | Send this 
article to a friend Mail this to a friend


Previous Next vertical dots separating previous/next from contents/index/pdf Contents
Nested Classes

Nested Classes


The Java programming language allows you define a class within another class or interface. Such a class is called a nested class. A nested class is any class whose declaration occurs within the body of another class or interface. A top level class is a class that is not a nested class.

class EnclosingClass {
    ...
    class ANestedClass {
        ...
    }
}

You use nested classes to reflect and to enforce the relationship between two classes. You should define a class within another class when the nested class makes sense only in the context of its enclosing class or when it relies on the enclosing class for its function.

Outer classes can access all the static members of their member classes, and with an object all the non-static members too.


Static Nested Classes


A static nested class is a nested class that is declared with the keyword static on the class's declaration. A static nested class can only access the various static members of the enclosing class. It does not have a pointer to the outer object and does not automatically access the instance member of the outer object.

package com.xyzws.nested;
public class Outer {
  private static int privatestaticVar  = 1;
  protected static int protetedstaticVar = 2;
  public  static int publicstaticVar   = 3;
  public  int publicVar;
	
  public static class staticNested {
    public void print() {
      System.out.println("privatestaticVar  = " 
                   + privatestaticVar);
      System.out.println("protectedstaticVar  = "
                   + protetedstaticVar);
      System.out.println("publicstaticVar  = " 
                   + publicstaticVar);
      /* compile-time error. You can not automatically 
         access instance variables.

      System.out.println("publicVar  = " 
                   + publicVar);
      */

    }
  }
}

As with static methods and variables, which we call class methods and variables, a static nested class is associated with its enclosing class. And like class methods, a static nested class cannot refer directly to instance variables or methods defined in its enclosing class but it can use them only through an object reference.

Thus, static nested classes can access all the static members of their outer class and other member classes, and with an object all the non-static members too.


How to Access a Static Nested Classes


The following is an example of accessing the public static nested class, listed in the above example. To instantiate a public static nested class, use both the outer and nested class names in the following way:

package com.xyzws.nested;
public class OuterTest {
  public static void main(String args[]) {
    Outer.staticNested s = new Outer.staticNested();
    s.print();
  }
	
}

or to instantiate a public static nested class, you do not need an instance of the outer class with the import statement.

package com.xyzws.nested;
import com.xyzws.nested.Outer.staticNested;
public class OuterTest {
  public static void main(String args[]) {
    staticNested s = new staticNested();
    s.print();
  }
	
}

Access Modifiers


The access modifiers for static nested classes include public, private, protected, final, abstract, and none (no modifers).

The private and protected static nested classes have predictable visibility and specialized functions. Generally,they are used to implement internal features of a class or class hierarchy.

For example, we change the staticNested class to private static nested class:

package com.xyzws.nested;
public class Outer {
  private static int privatestaticVar = 1;
  private static int protetedstaticVar = 2;
  public static int publicstaticVar = 3;
  public  int publicVar;
	
  private static class staticNested {
    public void print() {
      System.out.println("privatestaticVar  = " 
                   + privatestaticVar);
      System.out.println("protectedstaticVar  = "
                   + protetedstaticVar);
      System.out.println("publicstaticVar  = " 
                   + publicstaticVar);
    }
  }
  //Outer class can access the members of the private static nested classes
  public void accessPrivateStaticNested() {
    new staticNested().print();
  }
  
}

then our test code will not compile,

package com.xyzws.nested;
public class OuterTest {
  public static void main(String args[]) {
    Outer.staticNested s = new Outer.staticNested(); //compile-time error
    s.print();
  }
	
}

and

package com.xyzws.nested;
import com.xyzws.nested.Outer.staticNested; //compile-time error
public class OuterTest {
  public static void main(String args[]) {
    staticNested s = new staticNested(); //compile-time error
    s.print();
  }
	
}

Inner Classes (Non-Static Nested Classes)


An inner class is a type of nested class that is not explicitly or implicitly declared static. Inner classes are scoped to the instances of classes used to declare them -- thus they are effectively "invisible" to the other classes in the same package. The only way you can access the inner class is through an instance of its outer class.

This lack of visibility to other classes in the package gives the programmer the opportunity to create a set of classes within a containing class without cluttering up the name space of classes in their package. Use an inner class when you need one or more separate objects where it makes sense for that object be closely related to some outer object.

There are three kinds of inner classes: non-static nested classes, local classes and anonymous classes (also called anonymous inner classes). All inner classes objects are related to their outer-class object. The inner class objects must be created in the context of their outer-class object, and a connection to that outer object is maintained throughout the lifetime of the non-static member-class object.

An inner class can access all the members of an instance of its outer class. For this reason, an instance of inner class can only be created in association with an instance of its outer class.

The outer and inner classes can access each other's state, even if it is private. Stylistically, they are basically one implementation code base, so mixing access is ok, but we prefer receiver-relative coding for both the outer and inner classes where it makes sense.

public class Outer {
  private int x;
  public class Inner { // inner class
    private int num; 
    private void foo() {
      num++; // can access our regular inner ivar
      x = 13; // we can "see" our outer class automatically
      Outer.this.x = 13; // same as above
    }
  }

  public void test() {
    x = 10;
    Inner in = new Inner();
    in.foo(); // can see things, even if private
    ...
  }
}

Inner classes may not declare static initializers or member interfaces. Inner classes may not declare static imembers, unless they are compile-time constant fields.

To illustrate these rules, consider the example below:

class HasStatic{
    static int j = 100;
}
class Outer{
    class Inner extends HasStatic{
        static final int x = 3;    // ok - compile-time constant
        static int y = 4;    // compile-time error, an inner class
        static {};           // compile-time error, static initializers
        
	}
    static class NestedButNotInner{
        static int z = 5;    // ok, It is a static nested class
    }
    interface NeverInner{}    // interfaces are never inner
}

Inner classes may inherit static members that are not compile-time constants even though they may not declare them. Nested classes that are not inner classes may declare static members freely, in accordance with the usual rules of the Java programming language. Member interfaces are always implicitly static so they are never considered to be inner classes.


How to Create and Access Inner Classes


The inner class objects must be created in the context of their outer-class object.  An instance of inner class can only be created in association with an instance of the outer class,using the expression

Outer.Inner inner = instanceOuter.new Inner(...);

to instantiate inner object outside the outer class instance code, where instanceOuter is an instance of Outer, and

Inner inner = new Inner(...); 

to instantiate an inner class from inside of Outer's methods.

class Outer {
    private int m = 2;

    public void createInnerInsideOuter() {
        Inner inner = new Inner();
        inner.print();
        }
    
    public class Inner {
        public void print() {
            System.out.println("m = " + m);
        }
    }
}

class OuterTest {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        //the following code is the same as the above
        //Outer.Inner inner = new Outer().new Inner(); 
        inner.print();
    }
}

From outside the outer class instance code, the full name of the Inner class is "Outer.Inner" with classes named "Outer" and "Inner". The "new" call to make the inner is done from the context of a method of the outer object. - Calling "new" just anywhere will not work to create an inner object, because of the need for an outer object. The very obscure syntax "outer.new Inner()" can create object of class Inner owned by an "outer" object.

The new instance of the  inner class also knows about the enclosing instance of outer class and can refer to it using the expression,

Outer.this

"Outer.this" refers to the "this" pointer of the outer object ("Outer" being the name of the outer class).  We can think of an instance of an inner class as having two this references, one for itself and one for its enclosing instance. An inner class may be nested within another inner class, so an inner class can even have multiple levels of this references. Nesting inner classes more deeply than one level is quite uncommon, however, and should usually be avoided.


Access Modifiers


The inner class may or may not be exposed for use by clients. For example, a BinaryTree class might have an inner "Node" class that it uses internally to build the tree. The Node class is probably not exposed to clients -- it's just for internal use.

A non-static nested class is a member of the outer class. The access modifiers for non-static nested classes include public, private, protected, final, abstract, strictfp, and none (no modifers).

Private and protected inner classes have predictable visibility and specialized functions. Generally,they are used to implement internal features of a class or class hierarchy.


Method-Local Inner Classes


The normal inner class is scoped inside another class's body, but outside any method code. A method local class is a nested class that is not a member of any class, has a name , and enclosed in a class's method. The method local inner classes are scoped to a particular block of code. The method local class declaration statement and usage are contained by the block. Once declared,you can use the class throughout the remainder of the method just like any other class.

The instances of method-local class cannot use the local variables of its enclosing method unless the local variables are defined as final.

public class Outer {
  private int x;

  public void someMethod(final int y1, int y2) {
    final int z1 = 0;
    int z2; 
    ....
    class LocalInner {

       public void accessTest() {
 
         System.out.println("Outer class variable x =  " + x); 
         System.out.println("Method variable y1 =  " + y1);  // valid      
         System.out.println("Method variable y2 =  " + y2);  // invalid
         System.out.println("Method Local variable z1 =  " + z1);  // valid      
         System.out.println("Method Local variable z2 =  " + z2);  // invalid

       } 
    }
  }
}

The above example shows that local inner class has access to its outer class member variables even they are defined as private. It also has access to final variables declared within the block in which it is declared.

A method-local class may be defined in a static method. In this case, the instance of method-local inner class can only access the static members of the enclosing class.


How to Instantiate Method Local Inner Classes


A method-Local class can be instantiated only within the method where the inner class is defined.


Access Modifiers


The access modifier for method local inner classes can only have abstract, final, and default (none modifier).

Anonymous Inner Class


An anonymous inner class is a type of inner class created on the fly with a quick-and-dirty syntax. An anonymous inner class can extend a non-final superclass or implement an interface (but can not do both at the same time), just like a regular inner class. Anonymous classes can be used only once, so they must be used as a parameter to a method or assigned to a variable.

Anonymous inner classes are convenient for creating small inner classes. If the inner class involves longer code, then a regular inner class is a better choice.

Anonymous inner class does not have name, but it may be stored in a Superclass type pointer. The anonymous inner class has access to the outer class's variables, as usual for an inner class. When compiled, anonymous inner classes are given names like Outer$1, Outer$2 by the compiler.

interface PrintHelper {
  void print(int n);
}

public class Anonymous
{
  private int m=0;
  public Outer() {
  }
  
  public Outer(int m) {
    this.m = m;
  }
  
  PrintHelper getHexPrintHelper() {
    return new PrintHelper() {
      public void print(int n) {
        System.out.format("%x\n", n);
      }
    };
  }
  
  PrintHelper getDecPrintHelper() {
    return new PrintHelper() {
      public void print(int n) {
        System.out.format("%d\n", n);
      }
    };
  }
  
  void printHex(PrintHelper h) {
    h.print(m);
  }

  void printDec(PrintHelper h) {
    h.print(m);
  }
  
  public static void main( String args[] )
  {

    Anonymous o = new Anonymous();
    PrintHelper decP = o.getDecPrintHelper();
    dec.print(10);
    PrintHelper hexP = o.getDecPrintHelper();
    hexP.print(10);

    //Anonymous class access Outer variable
    Anonymous o1 = new Anonymous(10);
    o1.printDec(new PrintHelper() {
      public void print(int n) {
        System.out.format("%d\n", n);
      }});
    o1.printHex(new PrintHelper() {
      public void print(int n) {
        System.out.format("%x\n", n);
      }});
  }
}

The anonymous inner class does not have access to local stack variable from where it is declared, unless they are declared final. For example:

  PrintHelper getHexPrintHelper() {
    final String name = "HexPrintHelper : ";
    return new PrintHelper() {
      public void print(int n) {
        System.out.format(name + "%x\n", n); //OK
      }
    };
  }
  
  PrintHelper getDecPrintHelper() {
    String name = "DecPrintHelper : ";
    return new PrintHelper() {
      public void print(int n) {
        System.out.format(name + "%d\n", n); //compile-time error
      }
    };
  }

Anonymous inner classes cannot see local variables from where they are created (i.e. stack allocated variables). The stack variable will be gone when we are out of the scope of its method, but we still have the instance of anonymous class and can use it later. For example, the local variables no longer exist after calling getDecPrintHelper. That's why you can't refer to them from inside the anonymous inner code. When the anonymous inner code runs, that context no longer exists.

However, inner classes can see "final" stack variable from where they are created -- so create final stack variable to communicate values to an anonymous inner class.

An anonymous inner class cannot have a constructor but you can pass arguments (to call implicit constructor of its super class.). It must rely on the default object initialization behavior. The parameters to the superclass constructor immediately follow the supertype. This allows you to pass arguments to the constructor of the superclass of your anonymous class. If there are multiple constructors to the superclass, you can pass parameters that are appropriate for any one of those constructors. For example,

JButton btn = new JButton("Beep") {
   // ...contents omitted
};

In this case,the supertype of the anonymous class is javax.swing.JButton. The JButton class has several constructors; however, the programmer chose to pass arguments to the constructor that takes a single String parameter.


Access Modifier


Only default modifier permits to an anonymous class declaration.

An anonymous class is never abstract. An anonymous class is always an inner class; it is never static. An anonymous class is always implicitly final.


Previous Next vertical dots separating previous/next from contents/index/pdf Contents

  |   |