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 tog).
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
prefixanywhere 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:
| Situation | Prefer |
|---|---|
| Single abstract method, no local state | Lambda |
| Need multiple methods | Anonymous inner class |
| Need instance fields | Anonymous inner class |
| Need to extend an abstract class | Anonymous inner class |
| Need constructor arguments on the supertype | Anonymous inner class |
Implementing a Comparator inline | Lambda (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
privatefields 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!
Related Topics
- 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).