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
| 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 | Can only be used within that method |
| Anonymous inner class | Declared and instantiated simultaneously | Used 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
Builderpattern andEntryclasses (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!"));
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:
Builderpattern, 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.