Inner Classes — Static Nested, Inner, Local, Anonymous, Lambdas — Java Interview Guide | Cracked Java
Mid

Inner Classes — Static Nested, Inner, Local, Anonymous, Lambdas

Four kinds of inner classes, what each can capture, when to pick which, and how lambdas relate.

Prereqs: classes-constructors-initialization

Java has four kinds of nested classes — static nested, inner (non-static), local, and anonymous — distinguished by what they capture and where they can be declared. Lambdas are not a fifth kind; they are a more compact representation of a subset of what anonymous classes can do, with subtly different this semantics.

The four kinds at a glance

KindWhere declaredHas enclosing this?Captures locals?Typical use
Static nestedInside another class, marked staticNon/aHelper grouped with its owner (Map.Entry)
Inner (non-static)Inside another classYesn/aHelper that needs the enclosing instance
LocalInside a method/blockSometimesEffectively finalOne-off helper inside a method
AnonymousAn expression, no nameSometimesEffectively finalOne-off instance of a type with a custom override

The "sometimes" rows depend on whether the enclosing method is static.

What "captures the enclosing this" means

An inner class instance secretly stores a reference to its enclosing instance — javac generates a synthetic field called this$0. That hidden reference is why an inner class can write outerField without qualification: it's really this$0.outerField. It is also why an inner class can keep its enclosing instance alive long after you thought it was gone (see the memory-leak question).

Lambdas vs anonymous classes

Runnable a = new Runnable() { public void run() { System.out.println(this); } };
Runnable b = () -> System.out.println(this);

In a, this is the anonymous Runnable. In b, this is the enclosing class's this — the lambda has no this of its own. Lambdas are also compiled with invokedynamic + LambdaMetafactory, often producing zero allocations for non-capturing lambdas; anonymous classes always produce a new class file at compile time and a new object per use.

The local-capture rule

Local and anonymous classes (and lambdas) can capture local variables only if those variables are effectively final — assigned exactly once. This is because the JVM passes captures by value into a hidden field; if the local could change after capture, the inner instance would see a stale copy.

What you should reach for first

  • Records for data carriers.
  • Lambdas for one-method callbacks.
  • Static nested classes for tightly-coupled helpers.
  • Inner / local / anonymous rarely — most modern code can avoid them.

The questions below cover capture semantics, the static-vs-inner footprint, the effectively-final rule, the lambda conversion, and the most common Android/Swing memory leak in the JVM ecosystem.

Questions

5 in this topic