Java 48: Nested, Inner, Local & Anonymous Classes
easy⏱ 5 mincoursejava
Static nested vs inner
A static nested class (static class Node) is just a top-level class scoped inside another — no link to an outer instance, instantiate with Outer.Node. A (non-static) inner class (class Iter) holds an implicit Outer.this reference, so it can read the outer's fields — but it can only be created from an outer instance (outer.new Iter()) and keeps the outer alive for GC. Default to static unless you truly need the enclosing instance.
class Outer {
private int x = 10;
static class Nested { /* no Outer.this */ }
class Inner { int read() { return x; } } // captures Outer.this
}
Outer.Nested n = new Outer.Nested();
Outer o = new Outer();
Outer.Inner i = o.new Inner();
Local & anonymous classes
A local class is declared inside a method body; an anonymous class (new Runnable() { ... }) is a local class with no name, instantiated inline — perfect for one-shot interface implementations before lambdas existed. Both can capture local variables, but only if those are final or effectively final (never reassigned), because the captured value is copied into the class instance.
Runnable r = new Runnable() { // anonymous class
public void run() { System.out.println(prefix); } // captures prefix
};
// prefix must be effectively final
this and shadowing
Inside an inner/anonymous class, this refers to the inner instance; reach the enclosing instance with Outer.this. A lambda is different: it has no this of its own and captures the enclosing this — which is exactly why lambdas can't be self-referential and why they're cheaper than anonymous classes (no extra class, no captured-instance shadowing).
Model the four nested forms
Build Outer with a private field secret. (1) A static-nested Box that does NOT see secret. (2) An inner Reader created from an outer instance that DOES read secret (capture the outer in its constructor to model Outer.this). (3) A method returning an anonymous Greeter that captures an effectively-final name. (4) A factory making a counter via a captured local. Print each result and confirm the inner class reads the outer's secret.
Inner classes leak their outer
Because a non-static inner instance pins its Outer.this, storing an inner instance (e.g. a listener or Runnable) in a long-lived collection keeps the whole outer object — and everything it references — uncollectable. The classic Android/Swing leak. Fix: make the nested class static and pass only the data it needs.
Quiz: why effectively final?
Why must captured locals be (effectively) final? The class instance captures a copy of the value at creation, not the live variable — there's no shared mutable cell. Allowing reassignment would make the inner copy and the outer variable silently diverge. Need shared mutation? Use a one-element array or an AtomicInteger.