Nested and Anonymous Classes

This section documents the current Java release line, with Java 25 LTS as the reference point (no specific patch version is pinned), as published at the Java developer portal, the Java Tutorials, and the Java SE API specification — which are the references these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

A nested class is a class declared inside another class. Java has four kinds and they exist to keep a helper type next to the one place that uses it. This page follows the Java Tutorials "Nested Classes" lesson and Anonymous Classes.

Kind Enclosing instance? Typical use

static nested

no

a helper type namespaced under the outer class

inner (non-static)

yes — Outer.this

needs the outer object’s state: iterators, live views

local

yes

a named helper needed inside a single method

anonymous

yes

a one-off implementation of an interface or subclass

static Nested Classes

A static nested class is really a top-level class that happens to live inside another for namespacing. It holds no reference to an instance of the outer class and can reach only the outer class’s static members. Use it for a helper conceptually owned by the outer type — a builder, a key, a linked-list node.

public class LinkedStack<E> {

    // static: a Node never needs to see a particular LinkedStack instance
    private static final class Node<E> {
        final E value;
        Node<E> next;

        Node(E value, Node<E> next) {
            this.value = value;
            this.next = next;
        }
    }

    private Node<E> head;

    public void push(E value) {
        head = new Node<>(value, head);
    }

    public E pop() {
        if (head == null) {
            throw new java.util.NoSuchElementException("empty");
        }
        E value = head.value;
        head = head.next;
        return value;
    }
}

A static nested class is referred to as Outer.Nested and, where visible, instantiated with no outer object: new LinkedStack.Node<>(…​). A generic nested class declares its own type parameters, as Node<E> does above.

Inner (Non-static) Classes

Drop static and the class becomes an inner class. Every instance is bound to an instance of the enclosing class and can use its members directly, including private ones. The enclosing instance is named explicitly as Outer.this.

import java.util.ArrayList;
import java.util.List;

public class Rolodex {

    private final List<String> names = new ArrayList<>();

    public void add(String name) {
        names.add(name);
    }

    // inner class: each Cursor belongs to exactly one Rolodex
    public class Cursor {
        private int index = 0;

        public boolean hasNext() {
            return index < names.size();       // names is Rolodex.this.names
        }

        public String next() {
            return names.get(index++);
        }

        public Rolodex owner() {
            return Rolodex.this;               // the explicit enclosing instance
        }
    }

    public Cursor cursor() {
        return new Cursor();
    }
}

From inside the outer class you write new Cursor(); from outside you qualify it with the outer object using the outer.new Inner() form:

var rolodex = new Rolodex();
rolodex.add("Ada");
rolodex.add("Bo");

Rolodex.Cursor c = rolodex.new Cursor();     // outer.new Inner()
while (c.hasNext()) {
    System.out.println(c.next());
}

Since Java 16 an inner class may also declare its own static members; before that only compile-time constant static final fields were allowed.

The memory-leak caveat

Because an inner-class instance holds a hidden reference to Outer.this, it keeps the entire enclosing instance reachable for as long as the inner instance lives. Hand an inner-class object to something longer-lived than the outer object — a static registry, an event bus, a cache, a background executor — and the outer object, plus everything it holds, can no longer be garbage-collected. When the nested type does not truly need the enclosing instance, make it static and pass what it needs through the constructor.

Local Classes

A local class is a named class declared inside a method, constructor, or initializer block. It is visible only within that block and may capture local variables that are effectively final — assigned once and never reassigned. See Local Classes.

import java.util.ArrayList;
import java.util.List;

static List<String> render(List<String> lines, String prefix) {

    class Row {                                   // local class: visible only in render(...)
        final String key;
        final String value;

        Row(String raw) {
            int eq = raw.indexOf('=');
            this.key = prefix + raw.substring(0, eq).trim();   // captures prefix (effectively final)
            this.value = raw.substring(eq + 1).trim();
        }

        String line() {
            return key + " -> " + value;
        }
    }

    var out = new ArrayList<String>();
    for (var line : lines) {
        if (line.contains("=")) {
            out.add(new Row(line).line());
        }
    }
    return out;
}

A local class can see the enclosing method’s parameters and locals (if effectively final) and the enclosing instance’s members. Reach for one only when the helper needs a name and more than a single expression; otherwise a lambda is lighter.

Anonymous Classes

An anonymous class fuses the declaration and one instantiation into a single expression: it implements an interface or extends a class on the spot, with no name.

import java.util.Comparator;
import java.util.concurrent.atomic.AtomicInteger;

// implements an interface inline
Comparator<String> byLengthThenText = new Comparator<>() {
    @Override
    public int compare(String a, String b) {
        int byLen = Integer.compare(a.length(), b.length());
        return byLen != 0 ? byLen : a.compareTo(b);
    }
};

// extends a class inline, adding its own state
var trackingCounter = new AtomicInteger() {
    int toStringCalls = 0;

    @Override
    public String toString() {
        toStringCalls++;
        return "value=" + get() + " (toString calls: " + toStringCalls + ")";
    }
};

Like a local class, an anonymous class captures effectively-final locals. Inside it, this refers to the anonymous instance itself, not the enclosing object — reach the enclosing object as Enclosing.this. The new Comparator<>() diamond form (Java 9+) lets the compiler infer the anonymous class’s type argument.

Anonymous class or lambda?

If the target is a functional interface (one abstract method) and you need no extra state and no name, a lambda is shorter and cheaper — see Lambdas and Method References. Choose an anonymous class when you need something a lambda cannot provide:

  • more than one method to implement or override — for example overriding both toString and another method, or implementing a multi-method callback interface;

  • instance fields of its own to carry state between calls;

  • to extend a class rather than implement an interface;

  • a body where this must mean the new object rather than the enclosing one.

// a Runnable has one method and no state: prefer the lambda
Runnable viaAnonymous = new Runnable() {
    @Override
    public void run() {
        System.out.println("tick");
    }
};
Runnable viaLambda = () -> System.out.println("tick");   // same effect, no new class scope

See Also