Anonymous classes in Java
One-off implementations; how they differ from lambdas (this, state).
new Runnable() { ... } thousands of times. Is that old style dead — or can it still do things lambdas can't?Declare and create in one go
An anonymous class declares a one-off class and creates its only object in a single expression: new Interface() { ... } or new SomeClass() { ... }. It has no name, so you can't reuse it anywhere else.
Runnable r = new Runnable() {
public void run() {
System.out.println("running");
}
};What lambdas can't do
Unlike a lambda, an anonymous class can have its own fields, several methods, and can extend a class — even an abstract one — or implement an interface with two or more abstract methods. Like lambdas, it can capture effectively final locals.
A counter with memory
What does this print?
void main() {
var c = new Supplier<Integer>() {
int n = 0;
public Integer get() { return ++n; }
};
c.get();
c.get();
System.out.println(c.get());
}31Compile error: anonymous classes can't have fields
Show the answer
The anonymous object **keeps its own n** between calls: 1, 2, then 3. A lambda couldn't hold that state itself.
`this` means two different things
Inside a lambda, this is the enclosing object. Inside an anonymous class, this is the anonymous object itself. So this.id can read two different fields in code that looks almost the same.
String id = "outer";
Runnable lam = () -> print(this.id); // outer
Runnable anon = new Runnable() {
String id = "anon";
public void run() { print(this.id); } // anon
};Modernize it
Comparator<String> byLen =
new Comparator<>() {
public int compare(String a, String b) {
return a.length() - b.length();
}
};Six lines of boilerplate — and overflow-prone subtraction.
Comparator<String> byLen =
Comparator.comparingInt(String::length);Comparator is functional, so a factory method does the job safely.
Where you still see them
Legacy Swing and Android listeners are full of anonymous classes. Today they still earn their place when you need state or must extend an abstract class — e.g. new TimerTask() { public void run() { ... } }, because TimerTask is a class, not an interface.
Key takeaways
- new Runnable() { public void run() { ... } }
- Can hold its own fields and implement several methods
- this = the anonymous object (in a lambda, this = the enclosing object)
- Captures effectively final locals, like lambdas
Each anonymous class becomes its own file — Main$1.class, Main$2.class… Lambdas don't: since Java 8 they're linked at runtime through invokedynamic.
Practice questions
What does this print?
void main() {
var counter = new Supplier<Integer>() {
int n = 0;
public Integer get() { return ++n; }
};
counter.get();
System.out.println(counter.get());
}- 2
- 1
- 0
- Compile error: anonymous classes can't have fields
Check your answer
2. The anonymous object keeps its own n between calls: the first get() returns 1, the second returns 2.
What does this print?
String id = "outer";
void main() {
Runnable lam = () -> System.out.println(this.id);
Runnable anon = new Runnable() {
String id = "anon";
public void run() { System.out.println(this.id); }
};
lam.run();
anon.run();
}- outer anon
- anon anon
- outer outer
- anon outer
Check your answer
outer anon. In the lambda, this is the enclosing object (id = "outer"). In the anonymous class, this is the anonymous object (id = "anon").