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 encapsulation— Consider 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—
- Inner class (or Non-static nested class)
- Static Nested class
- Local Inner class
- 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.