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 membersprivate
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.
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.
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
andstrictfp
.- 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.
- Of course using the
- 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.
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.
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
orabstract
. - 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.
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 andimplement
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.
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.
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.
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.