Nested ClassesHomepage  « Learn Java5 « Nested Classes

This lesson is all about nested classes, which are classes defined within the curly braces of other classes. A nested class is only known to the enclosing class and shares its scope. This means that non-static nested classes have access to all the members and variables of the outer class. Conversely the outer class knows nothing of the internal working of the nested class.

There are many benefits to using nested classes and those relevant to the particular nested class are listed within the subsections below. The benefits that all nested classes receive are:

  • When a class has a specific purpose that is only relevant to one other class it makes sense to put the helper class within the class that uses it and we can use a nested class for this purpose.
  • Because nested classes have access to all the outer classes members, including members with the private access modifier, it gives us a way to keep outer class members private while still being able to access them, thus increasing encapsulation.
  • Having nested classes allows us to have inner classes close to the top-level classes that enclose them, making code easier to understand and maintain.

Nested classes can be static, which are known as static member classes, or non-static, which are known as inner classes. There are different types of inner class but only one type of static member class so let's look at the static version first.

Static Member Classesgo to top of page Top

A static member class is associated with its outer class in the same way that static members of the outer class would be. This means that a static member class cannot refer to non-static variables and methods defined within the outer class and can only interact with them through an object reference.

Behaviourally, static member classes act like any other top-level class and essentially are top-level classes that have been nested in another top-level class to facilitate packaging, or are associated with the outer class but it makes no sense to attach them to an instance of that class.

Static Member Instantiationgo to top of page Top

Following is example code for instantiation of a static member class:


/*
  An example of a static member class
*/ 
public class EnclosingClass {
    EnclosingClass.StaticMemberClass staticMemberObject = new EnclosingClass.StaticMemberClass();

    static class StaticMemberClass { 
        ... 
    }
}

We use the name of the outer class followed by the name of the inner class to instantiate the inner static class.

Static enum Examplego to top of page Top

The following code uses an enum as a static member class, we could have different static member entries for lunches and soups for instance:


/*
  A Recipes Class with a static member enum
*/ 
public class Recipes {
    /*
      Static Enumeration of soup (helper class)
    */ 
    public static enum Soup {
        TOMATO("vegetable") {  // Constant-specific class body 
            public String starRating() {  
                return "5 star rated"; 
            }
        },
        CHICKEN("meat"), 
        PRAWN("seafood") {  // Constant-specific class body  
            public String starRating() { 
                return "3 star rated"; 
            }
        };
        String type;
        /*
           enum constructor
        */ 
        Soup(String type) {
            this.type = type;
        }
        String getType() {
            return this.type;
        }
        /*
          default star rating
        */ 
        String starRating() {
            return "not rated yet";
        }
    }
}
/*
  Test Class for Recipes
*/ 
public class RecipeTest {

    public static void main (String[] args) {
        for (Recipes.Soup s : Recipes.Soup.values()) {
            System.out.println("We have " + s + " soup in our list, which is a " 
                                          + s.getType() + " soup and is " + s.starRating());
        }
    }
}

Save and compile the Recipes and RecipeTest classes and then run the RecipeTest class in directory   c:\_ObjectsAndClasses in the usual way.

run recipe test

The above screenshot shows the output of running our RecipeTest class. When an inner class is used as in the scenario above and has no relation to a particular instance of the enclosing class then favour the use of static member classes as you save space on resources.

Inner Classesgo to top of page Top

A nested class that is associated with an instance of its outer class, is known as an inner class. An inner class has access to all the methods and variables associated with the instance of the outer class including members with the private access modifier and can only exist within an outer instance.

the heap 10

There are three types of inner class, a non-static member class, which is a member of the outer class, just like the instance variables and methods are. The other two types are known as local inner classes and anonymous inner classes which exist within a method of the outer class. We will look at member inner classes first and then look at the inner classes associated with a method.

Non-static Member Classesgo to top of page Top

This type of inner class is a member of the outer class just like instance variables and methods are and as such the same modifiers and rules apply as to amy member. We can instantiate a non-static member class as part of the outer class state or seperately when you don't want all outer instances to have an inner instance.

  • Using non-static member classes is very useful when we require a class that doesn't pass the IS-A test but is intrinsicly associated with its enclosing class.
  • A non-static member class is a member of a class just like any other member and as such can have the same modifers applied in its class declaration: these being abstract, final, private, protected, public, static and strictfp.
    • Of course using the static keyword in the class declaration means we have turned the non-static member class into a static member class and so no longer have access to any instance variables and methods.
  • If the class is required by other classes as well, then the class would be better suited as a standard top-level class instead.

On-demand Examplego to top of page Top

Following is code to create a non-static member class instance from a method or from outside the class as required:


/*
  A Top-level Class
*/ 
public class Outer {
    private String a;
    
    public void createInner() {
        Inner inner = new Inner(); // Create inner class instance
        inner.changeInstanceVariables(); // Call inner class method
    }
    /*
      Non-static member class
    */ 
    class Inner {
         int b;
         void changeInstanceVariables() {
             a = "Updated";
             b = 1234;
             System.out.println("Outer variable a = " + a + ". Inner variable b = " + b); 
             System.out.println("Outer reference = " + Outer.this + ". Inner reference = " + this); 
         }
    }
}

Save and compile the file in directory   c:\_ObjectsAndClasses in the usual way. Lets write a new test class for our Outer class.


/*
  Test Class for Outer
*/ 
public class OuterTest {

    public static void main (String[] args) {
        Outer outer = new Outer();
        outer.createInner();
        /*
          Create inner class from outside the class
        */ 
        Outer outer2 = new Outer();
        Outer.Inner inner2 = outer2.new Inner();
        inner2.changeInstanceVariables();
    }
}

Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run outer test

The above screenshot shows the output of running our OuterTest class. We create an inner class by calling the createInner() method as and when we require the inner class. The second example shows the syntax for instantiating an inner class from outside the outer class and calling a method of the inner class. We also show the syntax for accessing the currently executing object using the this keyword for the outer and inner objects and print this off.

Always Instantiated Examplego to top of page Top

Ok, now lets look at code to instantiate a non-static member class as part of the outer class object state for those occasions when we always want an inner object attached to the outer object.


/*
  A Top-level Class
*/ 
public class Outer2 {
    private String a;
    Inner2 inner2 = new Inner2(); // Create inner class instance

    public void modifyState() {
        inner2.changeInstanceVariables(); // Call inner class method
    }
    /*
      Non-static member class
    */ 
    class Inner2 {
         int b;
         void changeInstanceVariables() {
             a = "Updated";
             b = 1234;
             System.out.println("Outer2 variable a = " + a + ". Inner2 variable b = " + b); 
             System.out.println("Outer2 reference = " + Outer2.this + ". Inner2 reference = " + this); 
         }
    }
}

Save and compile the file in directory   c:\_ObjectsAndClasses in the usual way. Lets write a new test class for our Outer2 class.


/*
  Test Class for Outer2
*/ 
public class Outer2Test {

    public static void main (String[] args) {
        Outer2 outer2 = new Outer2();
        outer2.modifyState();
    }
}

Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run outer2 test

The above screenshot shows the output of running our Outer2Test class. We create an inner class as part of the object state of the outer class and then call the modifyState() method to change the state of the outer and inner classes. We also show the syntax for accessing the currently executing object using the this keyword for the outer and inner objects and print this off.

Local Inner Classesgo to top of page Top

Local inner classes can be declared anywhere a local variable can be declared and have the same Method Scope. If you don't require the local inner class to be attached to an instance of the enclosing class, then the nested class should be declared in a static context. Local inner classes cannot contain static members and for readability the coding should be kept to a minimum.

  • The only valid modifers you can apply to a local inner class declaration are final or abstract.
  • Local inner classes can only be instantiated from within the method they are declared in.
  • When instantiating a local inner class, the instantiation code must come after the local inner class declaration.
  • The local inner class can only use final local variables defined within the method it resides in, outside those defined within itself and the outer instance.
  • When creating local inner classes and the code is quite robust, or the code needs to be accessible from more than one method, it is better to make a non-static member class.

Following is an example of a local inner class:


/*
  A Top-level Class
*/ 
public class Outer3 {
    private String a;
    public void modifyState() {
        /*
          Local inner class
        */ 
        class LocalInner {
            int b;
            void changeInstanceVariables() {
                a = "Updated";
                b = 1234;
                System.out.println("Outer3 variable a = " + a + ". LocalInner variable b = " + b); 
                System.out.println("Outer3 ref. = " + Outer3.this + ". LocalInner ref. = " + this); 
            }
        }
        LocalInner localInner = new LocalInner(); // Create local inner class
        localInner.changeInstanceVariables();
    }
}

Save and compile the file in directory   c:\_ObjectsAndClasses in the usual way. Lets write a new test class for our Outer3 class.


/*
  Test Class for Outer3
*/ 
public class Outer3Test {

    public static void main (String[] args) {
        Outer3 outer3 = new Outer3();
        outer3.modifyState();
    }
}

Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run outer3 test

The above screenshot shows the output of running our Outer3Test class. We create an outer class and then call a method that creates a local inner class instance and calls a method of the local inner class. We also show the syntax for accessing the currently executing object using the this keyword for the outer and inner objects and print this off.

Anonymous Inner Classesgo to top of page Top

The final type of inner classes we can use are anonymous inner classes which are different syntactically from anything else in Java and come with a lot of constraints:

  • Anonymous inner classes as the terminology implies have no name.
  • You can't execute the instanceof test against anonymous inner classes or any process that requires the name of the class.
  • Anonymous inner classes are not members of their enclosing class; in fact they are both declared and instantiated at the point of use.
  • Anonymous inner classes cannot contain any static members
  • Anonymous inner classes can be coded anywhere where an expression is legal, so keep the code to a minimum to maintain readability.
  • Anonymous inner classes can't be declared to extend a class and implement an interface
  • Anonymous inner classes can't implement multiple interfaces.
  • Anonymous inner classes can only invoke members inherited from the supertype

Subclass Examplego to top of page Top

Following is an example of using an anonymous inner class to override a method of the supertype.


/*
  A Top-level Class
*/ 
public class OuterAnon {
    
    public void aMethod() {
        System.out.println("Outer method"); 
    }
    public void anonymousSubclass() {
        /*
          Anonymous inner class
        */ 
        OuterAnon  outerAnon = new OuterAnon() {
            public void aMethod() {
                System.out.println("anonymous method"); 
            }
        }; // Here we close the anonymous inner class
        outerAnon.aMethod();
    }
}

The first thing to notice about the above code is that instead of ending the OuterAnon outerAnon = new OuterAnon() statement with a semi-colon we are actually writing a code block. What we are actually doing here is declaring a reference type of the OuterAnon supertype that doesn't refer to an instance of OuterAnon but to an anonymous subclass of it. All the override code for the supertype aMethod(), for our 'on the fly' subclass instance is held within the code block which is then followed by a semi-colon. In essence we are closing the OuterAnon outerAnon = new OuterAnon() statement with the semi-colon after doing whatever is required in our code block.

Save and compile the file in directory   c:\_ObjectsAndClasses in the usual way.

Lets write a new test class for our OuterAnon class.


/*
  Test Class for OuterAnon
*/ 
public class OuterAnonTest {

    public static void main (String[] args) {
        OuterAnon outerAnon = new OuterAnon();
        outerAnon.aMethod();
        outerAnon.anonymousSubclass();
    }
}

Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run outer anon test

The above screenshot shows the output of running our OuterAnonTest class. We create an outer class instance and call the aMethod() and output a message. We then call the anonymousSubclass() method where our anonymous inner class is declared and instantiated. Within this we override the supertype aMethod() method and output a message.

Implementer Examplego to top of page Top

We can also use anonymous inner classes as implementers of an interface. Following is the code for the PublicTransport interface we used in the last lesson:


/*
 The PublicTransport interface
*/ 
public interface PublicTransport {

    void queue(int queue);  // The public and abstract modifiers are added by the compiler
    void payFare(int money);  // The public and abstract modifiers are added by the compiler
}

Following is an example of using an anonymous inner class to declare and instantiate an implementer of the PublicTransport interface 'on the fly' whilst honouring the contract of the interface.


/*
  A Top-level Class
*/ 
public class OuterAnon2 {
    public void anonymousImplementer() {
        /*
          Anonymous inner class
        */ 
        PublicTransport pt = new PublicTransport () {
             // Our queue() implementation
             public void queue(int people) {
                 System.out.println("There are " + people + " people waiting at the taxi rank.");
        
             }
             // Our payFare() implementation
             public void payFare(int fare) {
                 System.out.println("This taxi ride will cost you " + fare + " pounds.");
             }
         }; // Here we close the anonymous inner class
         pt.queue(12);
         pt.payFare(3);
    }
}

Well this looks very strange it looks like we are instantiating an interface with the first line of our inner class PublicTransport pt = new PublicTransport(). But we know we can't instantiate an interface as it's like a 100% abstract class, so what is going on? Well what we are actually doing here is declaring a reference type of the PublicTransport interface supertype that refers to an anonymous implementer of the PublicTransport interface. The implementer gets instantiated with the the expression new PublicTransport(). All the implementation code for our new object is held within our code block which is then followed by a semi-colon.

Save and compile the file in directory   c:\_ObjectsAndClasses in the usual way.

Lets write a new test class for our OuterAnon2 class.


/*
  Test Class for OuterAnon2
*/ 
public class OuterAnon2Test {

    public static void main (String[] args) {
        OuterAnon2 outerAnon2 = new OuterAnon2();
        outerAnon2.anonymousImplementer();
    }
}


Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run outer anon test

The above screenshot shows the output of running our OuterAnon2Test class. We create an outer class instance and call the anonymousImplementer() method where our anonymous inner class is declared, instantiated and implemented.

Argument Implementer Examplego to top of page Top

In our final example of anonymous inner classes we look at how to pass an 'on the fly' instantiation and implementation of a method using an interface supertype as an argument. Before we go into this lets look at the Carrier class we created for the Polymorphic Interface Arguments section of the Interfaces lesson, as we will use it for the example:


/*
  A Carrier class
*/ 
public class Carrier {
    private int people;
    private int cost;

    Carrier(int people, int cost) {
        this.people = people;
        this.cost = cost;
    }

    public void travelInfo(PublicTransport pt) {
        pt.queue(people); 
        pt.payFare(cost); 
    }
}

As a refresher the travelInfo(PublicTransport pt) method of the carrier class above, takes a PublicTransport interface supertype as a parameter and outputs some messages for the implemented class. What we want to do is use an anonymous inner class to pass across an instantiation and implementation of the interface supertype as the argument. Following is code to do this:


/*
  A Top-level Class
*/ 
public class OuterAnon3 {
    public void anonymousArgImplementer() {
        Carrier cr = new Carrier(25, 4);
        /*
          Anonymous inner class
        */ 
        cr.travelInfo(new PublicTransport() {
             // Our queue() implementation
             public void queue(int people) {
                 System.out.println("There are " + people + " people waiting for a bus.");
             }
             // Our payFare() implementation
             public void payFare(int fare) {
                 System.out.println("This bus journey will cost you " + fare + " pounds.");
             }
         }); // Close anonymous inner class and method argument
    }
}

This looks even stranger than the last example so lets go through the code. The first part to look at is the cr.travelInfo( on the first line of the anonymous inner class declaration. Here we are calling the travelInfo() method using a Bus object. This method accepts a parameter of the PublicTransport interface supertype, so we need to pass an instance of that class along with an implementation of the class all within the argument. The new PublicTransport() part is where we create our 'on the fly' instance and the implementation follows within the {...} code block. We then close the anonymous inner class and we also need to close the method argument that contained it, before terminating the statement.

Save and compile the file in directory   c:\_ObjectsAndClasses in the usual way.

Lets write a new test class for our OuterAnon3 class.


/*
  Test Class for OuterAnon3
*/ 
public class OuterAnon3Test {

    public static void main (String[] args) {
        // Call the cr.travelInfo() method normally
        Carrier cr = new Carrier(10, 2);
        Bus bus = new Bus();
        cr.travelInfo(bus);
        OuterAnon3 outerAnon3 = new OuterAnon3();
        // Call the cr.travelInfo() method anonymously
        outerAnon3.anonymousArgImplementer();
    }
}

Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run outer anon test

The above screenshot shows the output of running our OuterAnon3Test class. We use the travelInfo(PublicTransport pt) method of the Carrier class the normal way by passing an instance of an implemented subtype of the PublicTransport interface supertype. We then use an anonymous inner class that creates an 'on the fly' instantiation and implementation of the PublicTransport interface supertype to be passed as the argument to the travelInfo(PublicTransport pt) method.

Nested Classes Quizgo to top of page Top

Try the quiz below to test your knowledge of this lesson

Question 1 : What are classes that are attached to an outer instance of the enclosing class collectively known as?
- classes that are attached to an outer instance of the enclosing class are collectively known as <em>inner classes</em>
Question 2 : Where are local inner classes declared?
- <em>local inner classes</em> are declared within a method.
Question 3 : What is class Inner in the following code snippet an example of?

public class OuterQuiz {
private String a;
public void createInner() {
Inner inner = new Inner(); // Create inner class instance
inner.changeInstanceVariables(); // Call inner class method
}
class Inner {
int b;
void changeInstanceVariables() {
a = "Updated";
b = 1234;
}
}
}
- class <code>Inner</code> is an example of a <em>non-static member class</em>.
Question 4 : Where can you use an anonymous inner class?
- You use an <em>anonymous inner class</em> anywhere where an expression is legal.
Question 5 : Which of the following statements is true for inner classes?
- We can never have just an inner instance, is the only true statement.
Question 6 : Assuming class A compiles, what is the result of trying to compile and run class ATest below?

public class A {
public void aMethod() { System.out.println("Outer method."); }
public void anonSubclass() {
A a = new A() {
public void aMethod() { System.out.println("Anonymous method."); }};
a.aMethod();
}
}
public class ATest {
public static void main (String[] args) {
A a = new A();
a.aMethod();
a.anonSubclass();
}
}
- class <code> ATest</code> compiles and runs fine and outputs "Outer method.Anonymous method.".
Question 7 : What is class Inner in the following code snippet an example of?

public class A { public void modifyState() { class Inner { int b; } } }
- class <code>Inner</code> is an example of a <em>local inner class</em>.
Question 8 : What local variables can a local inner class use?
- A <em>local inner class</em> can only use local variables that are marked as <code>final</code>.
Status Bar Please select an answer

What's Next?

In the next lesson we look at the 'daddy' of all classes, the Object superclass.

<<  Interfaces                    The Object Superclass  >>

go to home page Java5 Tutor Homepage go to top of page Top