6.5 Inner Classes
Defining a class inside another class is called an inner class. It is used to logically group code that is closely related to a specific class and unnecessary for the outside world.
Types of Inner Classes
| Type | Declaration Location | Characteristics |
|---|---|---|
| Member Inner Class | As a class member | Can access all members of the outer class |
| Static Nested Class | With static keyword | Can only access static members of the outer class |
| Local Inner Class | Inside a method | Only usable within that method |
| Anonymous Inner Class | Class declaration and instantiation at once | For one-time implementations |
1. Member Inner Class
The most common form. Can access all members of the outer class (including private).
public class Outer {
private String outerField = "Outer class field";
class Inner {
void display() {
System.out.println("Accessing from inner: " + outerField);
}
}
}
// Usage: must create outer class object first
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // Accessing from inner: Outer class field
2. Static Nested Class
A static inner class. Can be used without an outer class instance. Can only access static members of the outer class.
public class Database {
private static String url = "jdbc:mysql://localhost";
// Static nested class: commonly used in Builder pattern
static class Builder {
private String host = "localhost";
private int port = 3306;
private String dbName;
Builder host(String host) { this.host = host; return this; }
Builder port(int port) { this.port = port; return this; }
Builder dbName(String db) { this.dbName = db; return this; }
Database build() { return new Database(host, port, dbName); }
}
private Database(String host, int port, String dbName) {
System.out.printf("Connecting: %s:%d/%s%n", host, port, dbName);
}
}
// Usage: no outer class object needed
Database db = new Database.Builder()
.host("myserver.com")
.port(5432)
.dbName("shop")
.build();
3. Local Inner Class
Declared inside a method and only usable within that method. Rarely used; mostly replaced by lambda expressions.
public class LocalInnerExample {
void doSomething() {
final int localVar = 100; // must be effectively final
class LocalHelper {
void help() {
System.out.println("Using local var: " + localVar);
}
}
new LocalHelper().help(); // Using local var: 100
}
}
4. Anonymous Inner Class
A class with no name, instantiated at the point of declaration. Used for one-time implementations of interfaces or abstract classes. Mostly replaced by lambda expressions since Java 8.
interface Greeting {
void greet(String name);
}
// Anonymous inner class (pre-Java 8 style)
Greeting formal = new Greeting() {
@Override
public void greet(String name) {
System.out.println("Good day, " + name + ".");
}
};
// Lambda expression (Java 8+ recommended)
Greeting casual = name -> System.out.println("Hey, " + name + "!");
formal.greet("Alice"); // Good day, Alice.
casual.greet("Bob"); // Hey, Bob!
Inner class selection guide:
- Member inner class: When strongly dependent on outer instance state (e.g., custom iterator implementations)
- Static nested class:
Builderpattern, helper classes logically tied to the outer class → Most commonly used - Anonymous inner class: Interfaces not supported by lambdas (more than one abstract method) or legacy code
- Lambda expression: Always prefer lambdas for functional interfaces (single abstract method)
Memory warning: Member inner class instances implicitly hold a reference to the outer class instance. This can cause memory leaks. When the outer instance is not needed, always choose static nested class.