Anonymous Classes
BeginnerAnonymous classes are nameless one-shot class declarations that implement an interface or extend a class inline, without a separate file.
Overview
An anonymous class is a class defined and instantiated at the same time, without giving it a name. It can implement an interface or extend a class, and it can access effectively-final variables from the enclosing scope. Before Java 8 lambdas, anonymous classes were the idiomatic way to pass behaviour (callbacks, event listeners, comparators). Today, lambdas replace anonymous classes for functional interfaces, but anonymous classes remain useful when you need multiple methods or local state.
Anonymous Class Syntax
An anonymous class combines a class declaration with an object creation expression: new InterfaceOrClass() { ... }. It must implement all abstract methods. It can have fields, additional methods, and instance initializers — but no constructors (it's anonymous).
Variables captured from the enclosing scope must be effectively final.
// Anonymous class implementing an interface
Runnable r = new Runnable() {
private int runCount = 0; // can have fields
@Override
public void run() {
runCount++;
System.out.println("Run #" + runCount);
}
};
r.run(); // Run #1
r.run(); // Run #2
// Anonymous class extending an abstract class
abstract class Greeter {
abstract String greeting();
void greet(String name) {
System.out.println(greeting() + ", " + name + "!");
}
}
Greeter formal = new Greeter() {
@Override
String greeting() { return "Good day"; }
// inherits greet() from Greeter
};
formal.greet("Alice"); // Good day, Alice!
// Capturing effectively-final variable from enclosing scope
String prefix = "Hello"; // effectively final
Greeter casual = new Greeter() {
@Override String greeting() { return prefix; } // captures prefix
};Anonymous Classes vs Lambdas
For functional interfaces (single abstract method), lambdas are almost always preferred — they're more concise and readable. Anonymous classes remain necessary when: 1. The interface has multiple abstract methods. 2. You need local state (fields). 3. You need to call methods on this (referring to the anonymous class itself, not the enclosing class). 4. You need to pass to a method expecting a specific class, not an interface.
// Functional interface — prefer lambda
Comparator<String> byLength = (a, b) -> a.length() - b.length();
// vs anonymous class (verbose, same result)
Comparator<String> byLengthAnon = new Comparator<String>() {
@Override public int compare(String a, String b) {
return a.length() - b.length();
}
};
// Multiple methods — anonymous class required (or regular class)
MouseListener ml = new MouseAdapter() {
@Override public void mousePressed(MouseEvent e) { startDrag(e); }
@Override public void mouseReleased(MouseEvent e) { stopDrag(e); }
@Override public void mouseDragged(MouseEvent e) { updateDrag(e);}
};
// Local state — anonymous class useful
Iterator<Integer> counter = new Iterator<>() {
private int n = 0;
@Override public boolean hasNext() { return n < 10; }
@Override public Integer next() { return n++; }
};
// 'this' refers to anonymous class, not enclosing class
Runnable selfRef = new Runnable() {
@Override public void run() {
System.out.println("Class: " + this.getClass().getSimpleName());
// 'this' = the anonymous Runnable instance
}
};Anonymous Classes in Practice
Common real-world uses: Comparator (pre-Java-8), event listeners in Swing/Android, one-off Thread tasks, TypeReference pattern for generic type capture (Jackson/Gson), and test doubles.
The TypeReference trick is an important anonymous class use case that cannot be replaced by a lambda — it creates a subclass whose generic type parameters are preserved at runtime.
// TypeReference — preserves generic type at runtime
// This is the ONLY way to capture List<User> as a runtime type
List<User> users = objectMapper.readValue(json,
new TypeReference<List<User>>() {});
// Anonymous subclass retains type parameter via getGenericSuperclass()
// Comparable implementation in a test
List<Product> products = new ArrayList<>(List.of(
new Product("Laptop", 999.99),
new Product("Mouse", 29.99),
new Product("Monitor", 299.99)
));
// Sort by price descending (pre-Java-8 style — now use lambda)
Collections.sort(products, new Comparator<Product>() {
@Override public int compare(Product a, Product b) {
return Double.compare(b.price(), a.price());
}
});
// Modern equivalent
products.sort(Comparator.comparingDouble(Product::price).reversed());
// Thread with anonymous Runnable
new Thread(new Runnable() {
@Override public void run() {
System.out.println("Background task");
}
}).start();
// Lambda equivalent (preferred)
new Thread(() -> System.out.println("Background task")).start();Key Points to Remember
- Anonymous class = nameless class declared and instantiated inline: new Interface() { ... }.
- Can implement interfaces or extend classes; can have fields and methods but no constructors.
- Captures effectively-final variables from the enclosing scope.
- Use lambda for single-method functional interfaces; use anonymous class for multiple methods or local state.
- TypeReference<List<User>>(){} is an important anonymous class use case that cannot be a lambda.
Practice Anonymous Classes in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhen would you use an anonymous class instead of a lambda?
What does "this" refer to inside an anonymous class?
What is the TypeReference pattern and why must it be an anonymous class?
What variables can an anonymous class access from its enclosing scope?
What is the difference between an anonymous class and a local class?
Ask Aria about Anonymous Classes
Your personal AI tutor — ask anything about this concept