Skip to main content

6.6 Inner Classes

Defining a class inside another class is called an Inner Class. It is used to logically group code that is strongly associated with a specific class and unnecessary to the outside world.

Types of Inner Classes​

TypeDeclaration LocationCharacteristics
Member inner classAs a class memberCan access all members of the outer class
Static nested classWith static keywordCan only access static members of the outer class
Local inner classInside a methodCan only be used within that method
Anonymous inner classDeclared and instantiated simultaneouslyUsed for one-time implementations

1. Member Inner Class​

The most general form. Can access all members of the outer class (including private).

public class Outer {
private String outerField = "Outer class field";

class Inner {
void display() {
// Can access private field of the outer class!
System.out.println("Accessed from inner: " + outerField);
}
}
}

// Usage: must create an outer class object first
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // Accessed from inner: Outer class field

2. Static Nested Class​

An inner class with the static keyword. Can be used without an instance of the outer class. 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 the 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("Connected: %s:%d/%s%n", host, port, dbName);
}
}

// Usage: can be used directly without an outer class object
Database db = new Database.Builder()
.host("myserver.com")
.port(5432)
.dbName("shop")
.build();

The most commonly used form in production code. The Builder pattern and Entry classes (e.g., Map.Entry<K,V>) are representative examples.


3. Local Inner Class​

Declared inside a method and can only be used within that method. Rarely used; most cases are 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 variable: " + localVar);
}
}

LocalHelper helper = new LocalHelper();
helper.help(); // Using local variable: 100
}
}

4. Anonymous Inner Class​

A class declared and instantiated simultaneously without a name. Used to implement an interface or abstract class on a one-time basis. Since Java 8, most cases have been replaced by lambda expressions.

interface Greeting {
void greet(String name);
}

public class AnonymousExample {
public static void main(String[] args) {

// Anonymous inner class (pre-Java 8 style)
Greeting formal = new Greeting() {
@Override
public void greet(String name) {
System.out.println("Hello, " + name + ".");
}
};

// Lambda expression (recommended for Java 8+)
Greeting casual = name -> System.out.println("Hey, " + name + "!");

formal.greet("Alice"); // Hello, Alice.
casual.greet("Bob"); // Hey, Bob!
}
}

Practical Example: Event Handler (Swing Style)​

import javax.swing.*;
import java.awt.event.*;

JButton button = new JButton("Click!");

// Handle button click event with anonymous class
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button was clicked!");
}
});

// Same with lambda expression (Java 8+)
button.addActionListener(e -> System.out.println("Button was clicked!"));

Pro Tips

Inner Class Selection Guide:

  • Member inner class: When strongly dependent on the state of an outer instance (e.g., implementing an iterator)
  • Static nested class: Builder pattern, helper classes logically related to the outer class -> used most often
  • Anonymous inner class: Interfaces not supported by lambda (2+ abstract methods) or legacy code
  • Lambda expression: Always use lambda when implementing a functional interface (1 abstract method)

Memory Warning: A member inner class instance implicitly holds a reference to the outer class instance. This can cause memory leaks by keeping the outer object alive longer than expected. If the outer instance is not needed, always choose a static nested class.