Nested types: static nested, inner, local & anonymous
Four kinds of class-within-a-class — what each captures, when to use each.
Four kinds of nested type
### 1 — Static nested class
Declared static inside the outer class. Has no reference to the outer instance; it is just a logically grouped class:
public class Graph {
static class Edge { // static nested
int from, to, weight;
}
// usage:
Graph.Edge e = new Graph.Edge();
}
### 2 — Inner class (non-static nested)
No static keyword. Each instance holds a hidden reference to the enclosing outer instance. Accesses the outer instance with Outer.this:
public class Button {
private String label;
class ClickHandler { // inner class
void onClick() {
System.out.println(Button.this.label + " clicked");
}
}
}
Button b = new Button();
Button.ClickHandler h = b.new ClickHandler(); // note: b.new syntax
### 3 — Local class
Declared inside a method body. Can access local variables in scope — but only those that are effectively final (not reassigned after capture):
void process(String prefix) {
class Formatter { // local class
String fmt(String s) { return prefix + s; } // prefix is effectively final
}
new Formatter().fmt("hello");
}
### 4 — Anonymous class
A one-shot class with no name, defined and instantiated in a single expression. The pre-lambda way to implement a functional interface:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("running");
}
};
// equivalent lambda (Module 11):
Runnable r2 = () -> System.out.println("running");
Anonymous classes also capture effectively final local variables and have access to the enclosing instance.
Choosing which to use
| Kind | Has outer ref? | Named? | When | |---|---|---|---| | Static nested | No | Yes | Helper/data type logically grouped but independent | | Inner | Yes | Yes | Needs repeated access to outer state | | Local | Yes (eff. final) | Yes | Tiny helper used in one method only | | Anonymous | Yes (eff. final) | No | One-shot, usually replaced by lambdas today |