Skip to content
Java inner classes 6 min read

Anonymous Inner Class

An anonymous inner class is a class with no name — you define it and instantiate it at the same time, in a single expression. They’re ideal for one-off implementations of an interface or abstract class where creating a whole new top-level file would be overkill.

The Basic Syntax

The syntax looks a bit unusual at first glance. You write new InterfaceOrClass() { ... } — the curly braces contain the body of the new unnamed class.

interface Greeter {
    void greet(String name);
}

public class Main {
    public static void main(String[] args) {
        Greeter g = new Greeter() {
            @Override
            public void greet(String name) {
                System.out.println("Hello, " + name + "!");
            }
        };

        g.greet("Alice");
    }
}

Output:

Hello, Alice!

Notice that Greeter is an interface — you cannot instantiate an interface directly, but you can create an anonymous class that implements it, all in one go.

Note: The semicolon after the closing } is required because the whole thing is an expression statement (you’re assigning a value to g).

Extending a Class Anonymously

Anonymous inner classes can also extend an abstract (or even a concrete) class:

abstract class Shape {
    abstract double area();

    void printArea() {
        System.out.println("Area: " + area());
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape() {
            private final double radius = 5.0;

            @Override
            double area() {
                return Math.PI * radius * radius;
            }
        };

        circle.printArea();
    }
}

Output:

Area: 78.53981633974483

You can only extend one class or implement one interface per anonymous class. There is no way to write new Foo() implements Bar { ... }.

Accessing Outer Variables

An anonymous inner class can read variables from the surrounding scope, but only if those variables are effectively final — meaning they are never reassigned after their initial assignment.

public class Main {
    public static void main(String[] args) {
        String prefix = ">> ";   // effectively final

        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(prefix + "Running!");
            }
        };

        r.run();
    }
}

Output:

>> Running!

Warning: If you try to reassign prefix anywhere after its declaration, the compiler will refuse: “local variables referenced from an inner class must be final or effectively final.” This rule exists because the compiler copies the captured value into the anonymous class — a later reassignment would make that copy stale.

Adding State and Multiple Methods

Unlike lambdas, an anonymous inner class can have fields, multiple methods, and even initializer blocks. This is the main reason to reach for one over a lambda expression.

interface Animal {
    void makeSound();
    void move();
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Animal() {
            private int steps = 0;  // instance field inside anonymous class

            @Override
            public void makeSound() {
                System.out.println("Woof!");
            }

            @Override
            public void move() {
                steps++;
                System.out.println("Dog walked " + steps + " step(s).");
            }
        };

        dog.makeSound();
        dog.move();
        dog.move();
    }
}

Output:

Woof!
Dog walked 1 step(s).
Dog walked 2 step(s).

Anonymous Classes vs Lambda Expressions

Java 8 introduced lambda expressions, which are the concise replacement for anonymous classes that implement a functional interface (exactly one abstract method). Here’s a direct comparison:

// Anonymous inner class — verbose
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Anonymous class");
    }
};

// Lambda — concise (Java 8+)
Runnable r2 = () -> System.out.println("Lambda");

r1.run();
r2.run();

Output:

Anonymous class
Lambda

Use this table to decide which to reach for:

SituationPrefer
Single abstract method, no local stateLambda
Need multiple methodsAnonymous inner class
Need instance fieldsAnonymous inner class
Need to extend an abstract classAnonymous inner class
Need constructor arguments on the supertypeAnonymous inner class
Implementing a Comparator inlineLambda (cleaner)

Tip: Prefer lambdas for functional interfaces — they’re shorter, more readable, and don’t carry the hidden outer-instance reference that anonymous classes do.

Common Real-World Uses

Event Listeners (Swing / GUI)

Before lambdas, anonymous classes were the standard way to attach event handlers in Swing:

import javax.swing.*;
import java.awt.event.*;

JButton button = new JButton("Click me");
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button clicked!");
    }
});

With Java 8+ you would write button.addActionListener(e -> System.out.println("Button clicked!")), but you’ll see the anonymous-class form in a lot of legacy code.

Comparators

import java.util.*;

List<String> names = new ArrayList<>(Arrays.asList("Charlie", "Alice", "Bob"));

// Anonymous Comparator (pre-Java 8 style)
Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});

System.out.println(names);

Output:

[Alice, Bob, Charlie]

See Comparator and Sorting Collections for the modern lambda alternatives.

Thread Creation

Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Thread running: " + Thread.currentThread().getName());
    }
});
t.start();

Output:

Thread running: Thread-0

For more on threads, see Creating a Thread.

Under the Hood

Separate .class File

When you compile code containing an anonymous inner class, the Java compiler generates a separate .class file for it. The name is OuterClass$1.class for the first anonymous class, OuterClass$2.class for the second, and so on. You can verify this with javap:

$ javac Main.java
$ ls
Main.class   Main$1.class

Main$1.class is the compiled anonymous class. The number is assigned in lexical order of appearance in the source file.

Hidden Outer Reference

Like all non-static inner classes, an anonymous inner class holds a hidden synthetic reference — often called this$0 — pointing to the enclosing outer instance. This means:

  • The anonymous class can freely read private fields of the outer class.
  • The outer object cannot be garbage-collected as long as any anonymous class instance referencing it is alive.

This is a well-known source of memory leaks in Android and Swing when an anonymous listener (holding an outer Activity or Window) outlives the outer object. If you don’t actually need the outer reference, consider a static nested class or a lambda instead.

Captured Variables Are Copied

When an anonymous class captures an effectively-final local variable, the compiler copies the variable’s value into a synthetic field inside the anonymous class. There is no live link to the original variable — it’s just a snapshot taken at the time the anonymous class is created.

No Constructor

Anonymous classes cannot have an explicitly declared constructor (they have no name to give the constructor). If you need to run initialization logic, use an instance initializer block:

Runnable r = new Runnable() {
    private final String message;

    {   // instance initializer block
        message = "Initialized!";
        System.out.println("Anonymous class created");
    }

    @Override
    public void run() {
        System.out.println(message);
    }
};
r.run();

Output:

Anonymous class created
Initialized!
  • Inner Classes — the full overview of all four nested class types in Java.
  • Member Inner Class — a named non-static inner class with full access to the outer instance.
  • Lambda Expressions — the modern, concise replacement for single-method anonymous classes.
  • Functional Interfaces — the interface contract that makes lambdas (and anonymous classes) interchangeable.
  • Instance Initializer Block — the only initialization mechanism available inside an anonymous class.
  • Comparator — one of the most common interfaces implemented via anonymous classes (or lambdas).
Last updated June 13, 2026
Was this helpful?