Nested Static Member ClassesS2C Home « Nested Static Member 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 ClassesTop

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 InstantiationTop

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 ExampleTop

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 ClassesTop

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 ClassesTop

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 ExampleTop

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 ExampleTop

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 ClassesTop

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 ClassesTop

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 ExampleTop

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 ExampleTop

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 ExampleTop

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.

Lesson 7 Complete

In this lesson we learned about the various ways to code nested classes within our classes.

What's Next?

We finish the section by looking at the Object object, the daddy of all classes.