Java Nested Classes

Java allows us to define a class within another class. Such a class is called a nested class and is illustrated here:

class OuterClass {    
    ...    
    class NestedClass {        
        ...    
    }
}

 

Nested classes are divided into two main categories: static and non-static. Nested classes that are declared static are simply called static nested classes. Non-static nested classes are called inner classes.

 class OuterClass {    
      ...    

//static nested class static class StaticNestedClass {        ...    }   

//non-static nested class class InnerClass {        ...    } }

 

A nested class is a member of its enclosing class. Non-static nested classes (i.e. inner classes) have access to other members of the enclosing class, even if they are declared private.

Static nested classes do not have access to non-static members of the enclosing class.

As a member of the outer class, a nested class can be declared private, public, protected, or package private. But, note that outer classes can only be declared public or package private.

Additionally, there are two special kinds of inner classes: local inner class and anonymous inner class.

 

 

Need for Nested Classes

There are several compelling reasons for using nested classes, among them:

  • It is a way of logically grouping classes that are only used in one place.
  • It increases encapsulation.
  • Nested classes can lead to more readable and maintainable code.

Explanation:

Logical grouping of classes If a class is useful to only one other class, then it is logical to embed it in that class and keep the two together. Nesting such “helper classes” makes their package more streamlined.

Increased encapsulationConsider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A’s members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.

More readable, maintainable code Nesting small classes within top-level classes places the code closer to where it is used.

 

 

Types of Nested Classes

As a whole, nested classes are of 4 types—

  1. Inner class (or Non-static nested class)
  2. Static Nested class
  3. Local Inner class
  4. Anonymous Inner class

 

1. Inner class (Non-static nested class)

Inner class (or non-static nested class) is associated with an instance of its enclosing class and has direct access to that object’s methods and fields (even if they are private). It is quite similar to that of instance methods and instance fields of a class. That is why an inner class is called a member inner class.

Also, because an inner class is associated with an instance, it cannot define any static members itself.

Objects that are instances of an inner class exist within an instance of the outer class. Consider the following classes:

class OuterClass {    
    ...    
    class InnerClass {        
        ...    
    }
}

An instance of InnerClass can exist only within an instance of OuterClass and has direct access to the methods and fields of its enclosing instance.

To instantiate an inner class, we must first instantiate the outer class. Then, create the inner object within the outer object with this syntax:

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

 

Example 1

See the following example to demonstrate an inner class.

Nested01.java

class Outer {
private int outer_x = 100;

private void msg() {
System.out.println("Say hello!");
}

class Inner {
int inner_y = 10;

void show() {
System.out.println("outer_x: " + outer_x);
msg();
}
}// end of class Inner

void test() {
//System.out.println("inner_y: " + inner_y); //compilation error
Inner in = new Inner();
in.show();
//System.out.println("inner_y: " + in.inner_y); //valid operation
}

}// end of class Outer

public class Nested01 {

public static void main(String[] args) {
Outer out = new Outer();
out.test();
Outer.Inner obj = new Outer().new Inner(); //creating inner class object
System.out.println("inner_y: " + obj.inner_y);
}
}

Output:

outer_x: 100
Say hello!
inner_y: 10

Note: After compilation, the corresponding file name of the member inner class will be Outer$Inner.class within the working directory. 

 

2. Static Nested class

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

A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class. In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience.

Static nested classes are accessed using the enclosing class name:

OuterClass.StaticNestedClass

For example, to create an object for the static nested class, use this syntax:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

 

Example 2

See the example below to illustrate a static nested class.

Nested02.java

class Out { //Top-level class

static int x = 25; //static field
int y = 50; //non-static field

static class In { //Static nested class
void show() {
System.out.println("x: " + x);
System.out.println("y: " + new Out().y);
}
}
}

public class Nested02 {
public static void main(String[] args) {
Out.In obj = new Out.In();
obj.show();
}
}

Output:

x: 25
y: 50

Note: After compilation, the corresponding file name of the static nested class will be Out$In.class within the working directory. 

 

3. Local Inner class

We can declare an inner class within method body or inside a block.

Such an inner class is known as a local inner class.

It is basically a special type of inner class.

 

Example 3a

See the following example to demonstrate a local inner class. We have placed the local inner class named Second within a loop inside the method test() under top-level class First.

Nested03.java

class First { //Top-level class
private int x = 10;

void test() {
for (int i = 0; i < 5; i++) {

class Second { //Local inner class

void show() {
System.out.println("x = " + x);
}
}

Second sc = new Second();
sc.show();
} //end of for loop

} //end of method test()

}

public class Nested03 {

public static void main(String[] args) {
First f = new First();
f.test();
}
}

Output:

x = 10
x = 10
x = 10
x = 10
x = 10

Note: After compilation, the corresponding file name of the local inner class will be First$1Second.class within the working directory. 

 

Example 3b

The coding example below shows how a local inner class is defined within an instance initializer block under the outer class. It may also be defined within a static block.

Nested.java

public class Nested { //outer class

//instance initializer block
{
class Abc {  //local inner class
void show() {
System.out.println("local inner class within a block");
}
}
new Abc().show();
}

public static void main(String[] args) {
new Nested();
}
}

Output:

local inner class within a block

Note: After compilation, the corresponding file name of the local inner class will be Nested$1Abc.class within the working directory. 

 

4. Anonymous Inner class

We can also declare an inner class within the body of a method without naming it. These classes are known as anonymous inner classes.

This type of class can be generated from a super class or a super interface.

We will encounter such classes in this topic under “Java Multithreaded Programming”.

 

Example 4a

The following example shows how an anonymous inner class extends a superclass implicitly.

Nested04.java

class Parent { //super class
void show() { }
}

class Another { //Top-level class

Parent get() { //instance method of Another class

return new Parent() { //Anonymous Inner class
@Override
void show() {
System.out.println("show() method overridden");
}
};
}
}

public class Nested04 {

public static void main(String[] args) {
Another an = new Another();
Parent p = an.get();
p.show();
}
}


Output:

show() method overridden

Note: After compilation, the corresponding file name of the anonymous inner class will be Another$1.class within the working directory. 

 

Example 4b

The following example shows how an anonymous inner class implements a super interface implicitly.

Nest.java

interface Father { //super interface
void disp();
}

public class Nest { //outer class

Father f = new Father() { //anonymous inner class
@Override
public void disp() {
System.out.println("disp() method overridden");
}
};

public static void main(String args[]) {
Nest an = new Nest();
an.f.disp();
}
}

Output:

disp() method overridden

Note: After compilation, the corresponding file name of the anonymous inner class will be Nest$1.class within the working directory. 

 

 

Class within Interface

We can also define a nested class within an interface. See the following example.

NestedClass.java

interface Top { //top level interface

class Bottom { //nested class
void show() {
System.out.println("class within interface");
}
}
}

public class NestedClass extends Top.Bottom {

public static void main(String[] args) {
new NestedClass().show();
}
}

Output:

class within interface

Note: After compilation, the corresponding file name of the nested class will be Top$Bottom.class within the working directory. 

In the next topic, I will discuss about nested interfaces in Java.