Nested classes are classes defined within other classes. They allow logical grouping and better encapsulation. In Java, there are four types of nested classes:
- Inner Classes
- Static Nested Classes
- Local Classes
- Anonymous Classes
Types of Nested Classes in Java
Each nested class type in Java serves a specific purpose. Let’s explore them in detail, with practical examples and scenarios of when to use each.
Inner Classes in Java
An inner class is a class defined inside another class, known as the outer class. It has access to the instance variables and methods of its enclosing class. Inner classes are often used in situations where each instance of the inner class is tightly linked to a specific instance of the outer class.
// Outer class
public class OuterClass {
private int outerValue = 10;
// Inner class
public class InnerClass {
public void display() {
System.out.println("Outer value is " + outerValue);
}
}
}
In this example, the inner class accesses the private member of its enclosing outer class.
How do you create an instance of an inner class in Java?
To instantiate an inner class in Java, you first need an instance of the outer class. Since inner classes are tied to the outer class’s instance, they cannot exist independently, as the inner object holds an implicit reference to the outer object.
To create an instance of an inner class, start by instantiating the outer class. Then, use the outer class instance to instantiate the inner class using this syntax:
// Instantiating
OuterClass outerInstance = new OuterClass();
OuterClass.InnerClass innerInstance = outerInstance.new InnerClass();
innerInstance.display();
Real-World Example of Inner Classes: The Iterator Inner Class in Java’s ArrayList
A well-known real-life example of an inner class in Java’s standard library is the Iterator
inner class within the ArrayList
class. In Java, ArrayList
is a widely used implementation of the List
interface, and it provides an Iterator
inner class to enable sequential traversal of its elements.
The Iterator
inner class allows developers to iterate through the elements of an ArrayList
without exposing its internal structure. This encapsulation ensures that the internal workings of the ArrayList
remain hidden while providing an efficient mechanism to access its elements.
Here’s an example of how the Iterator
inner class is used with an ArrayList
:
import java.util.ArrayList;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
// Using the Iterator inner class to traverse the ArrayList
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
System.out.println("Fruit: " + iterator.next());
}
}
}
In this example, the Iterator
inner class provides methods like hasNext()
and next()
to traverse the elements of the ArrayList
sequentially. As an inner class, Iterator
can directly access the private fields and methods of the ArrayList
, such as its internal array storage and size, enabling efficient traversal without exposing these details to external code. This design maintains encapsulation while ensuring the ArrayList
remains internally consistent during iteration, even if the collection is modified.
Access Modifiers Allowed
An inner class in Java can be private
, public
, protected
, and package-private like another member.
💡 Tip: When defining a member class that doesn’t need access to its enclosing instance, always declare it as static
to indicate it is independent of the outer class’s instance.
Static Inner Classes in Java
Static nested classes are similar to static methods; they can be accessed without an instance of the outer class. Since they don’t have access to the instance variables of the outer class, they’re mainly used when the behavior of the nested class is independent of the outer class.
For example, the LinkedList
class in Java contains a static Node
class that represents each node in the linked list.
public class LinkedList<E> {
transient Node<E> first;
transient Node<E> last;
.......
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
}
Technically speaking, we should use static inner classes when the inner class does not need to access the members (variables or methods) of the outer class. A static inner class does not hold an implicit reference to an instance of the outer class. In other words, a static inner class serves as a helper to the outer class without relying on its instance.
Comparison Between Inner Classes and Static Inner Classes
Here’s a table that compares inner classes and static inner classes:
Feature | Inner Class | Static Inner Class |
---|---|---|
Reference to Outer Class | Has an implicit reference to the enclosing class instance. | Does not have an implicit reference to the enclosing class instance. |
Access to Outer Class Members | Can access both static and instance members of the outer class. | Can only access static members of the outer class. |
Memory Overhead | Each instance of the inner class holds a reference to the outer class, increasing memory usage. | Does not hold a reference to the outer class, reducing memory usage. |
Use Case | Best for logic tightly coupled with the enclosing class’s state or behavior. | Best for independent helper functionality that does not rely on the enclosing class’s instance. |
Encapsulation | Less encapsulated due to its dependency on the enclosing class. | More encapsulated and modular. |
Common Examples | Event listeners in GUI frameworks, callback handlers. | Data structures (e.g., Node in LinkedList ), utility classes. |
Local Classes
Local classes are nested classes defined within a method, accessible only within the scope of that method.
public class OuterClass {
public void calculate(int x) {
// Local class within a method
class LocalCalculator {
public int square() {
return x * x;
}
}
LocalCalculator calculator = new LocalCalculator();
System.out.println("Square is " + calculator.square());
}
}
Use Case
They are used in methods where you need a one-time-use class to organize logic. They have access to final or effectively final variables of the enclosing method.
Access Modifiers Allowed For Local Classes
Local classes cannot have explicit access modifiers (same as local variables)
Anonymous Inner Classes in Java
Anonymous inner classes are local classes without a name, usually declared and instantiated at the same time. They’re commonly used for short-lived implementations, often for interfaces or abstract classes.
interface Greeting {
void sayHello();
}
public class OuterClass {
public void createGreeting() {
Greeting greeting = new Greeting() {
@Override
public void sayHello() {
System.out.println("Hello from anonymous class!");
}
};
greeting.sayHello();
}
}
Access Modifiers Allowed
Cannot have explicit access modifiers since they’re defined within a method or initializer block.
Conclusion
Nested classes in Java offer a structured way to encapsulate functionality within the context of another class. By understanding when and how to use each type, you can organize your code efficiently and leverage Java’s OOP capabilities effectively.