Anonymous Classes
An anonymous class is a class without a name, declared and instantiated in a single expression. It either implements an interface or extends a class, and it is used exactly once, at the point where it is written. Anonymous classes were the standard way to pass behavior around in Java before lambdas arrived in Java 8, and they remain the right tool when the implementation needs more than a lambda can offer: several methods, its own state, or a superclass to extend.
Syntax
Type variable = new Type(constructorArgs) {
// fields, methods, and overrides of Type
};
Typeis the interface being implemented or the class being extended.- The parentheses hold arguments for the superclass constructor; for an interface they are always empty.
- The body in braces defines the class.
- The whole thing is an expression, so the statement ends with
;after the closing brace.
Implementing an Interface
public interface Greeter {
String greet(String name);
}
Greeter formal = new Greeter() {
@Override
public String greet(String name) {
return "Good morning, " + name + ".";
}
};
System.out.println(formal.greet("Dr. Rao")); // Good morning, Dr. Rao.
This single expression does three things: declares a new class that implements Greeter, creates one instance of it, and assigns that instance to formal. The class has no name you can refer to; the compiler names it something like Main$1.
Without an anonymous class, the same code would need a separate named class:
class FormalGreeter implements Greeter {
public String greet(String name) { return "Good morning, " + name + "."; }
}
Greeter formal = new FormalGreeter();
An anonymous class is worthwhile when the implementation is short and used in only one place.
Extending a Class
An anonymous class can extend a concrete or abstract class, passing arguments to its constructor:
public abstract class Task {
private final String name;
protected Task(String name) { this.name = name; }
public abstract void run();
public void execute() {
System.out.println("Starting " + name);
run();
System.out.println("Finished " + name);
}
}
Task backup = new Task("nightly-backup") { // "nightly-backup" goes to Task(String)
@Override
public void run() {
System.out.println(" copying files...");
}
};
backup.execute();
Output:
Starting nightly-backup
copying files...
Finished nightly-backup
Anonymous classes cannot declare constructors (they have no name to give one), so any setup goes through the superclass constructor arguments, field initializers, or an instance initializer block.
Capturing Variables from the Enclosing Scope
An anonymous class can use the enclosing method's local variables and parameters, as long as they are effectively final (never reassigned):
public static Greeter greeterFor(String language) {
String prefix = switch (language) {
case "ta" -> "Vanakkam";
case "hi" -> "Namaste";
default -> "Hello";
};
return new Greeter() {
@Override
public String greet(String name) {
return prefix + ", " + name + "!"; // captures prefix
}
};
}
System.out.println(greeterFor("ta").greet("Priya")); // Vanakkam, Priya!
The anonymous object outlives the method call that created it. Java copies prefix into the object, which is why the variable must not change afterward: a copy that silently diverged from the original would be confusing. Reassigning prefix anywhere in the method makes the code fail to compile.
Inside an instance method, an anonymous class can also access the enclosing object's fields and methods, because it holds a reference to that object, just as an inner class does.
State and Multiple Methods
Unlike a lambda, an anonymous class can have fields and implement more than one method:
import java.util.Iterator;
public static Iterator<Integer> countdown(int from) {
return new Iterator<>() {
private int current = from; // its own mutable state
@Override
public boolean hasNext() {
return current > 0;
}
@Override
public Integer next() {
return current--;
}
};
}
Iterator<Integer> it = countdown(3);
while (it.hasNext()) {
System.out.print(it.next() + " "); // 3 2 1
}
Iterator has two abstract methods, so a lambda cannot implement it. The anonymous class keeps current as a field and updates it on each call. Note new Iterator<>(): since Java 9, the diamond operator can be used with anonymous classes.
this Inside an Anonymous Class
Inside an anonymous class, this refers to the anonymous object, not the enclosing one. To reach the enclosing object, use OuterClass.this:
public class Download {
private final String file = "report.pdf";
public Runnable progressPrinter() {
return new Runnable() {
@Override
public void run() {
System.out.println(this.getClass().getName()); // Download$1
System.out.println(Download.this.file); // report.pdf
}
@Override
public String toString() {
return "progress for " + file; // outer field still accessible
}
};
}
}
Lambdas behave differently: a lambda does not create a new scope for this, so this inside a lambda means the enclosing object.
Anonymous Classes Versus Lambdas
When an interface has exactly one abstract method (a functional interface), a lambda is usually the better choice:
// Anonymous class
Comparator<String> byLength = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());
}
};
// Lambda: same behavior
Comparator<String> byLength2 = (a, b) -> Integer.compare(a.length(), b.length());
// Method reference with a factory: clearest of all
Comparator<String> byLength3 = Comparator.comparingInt(String::length);
| Anonymous class | Lambda | |
|---|---|---|
| Can implement | Any interface or class, with any number of methods | Only a functional interface (one abstract method) |
| Can extend a class | Yes | No |
| Fields and state | Yes | No (only captured, effectively final variables) |
Meaning of this | The anonymous object | The enclosing object |
| Creates a class file | Yes (Outer$1.class) | No separate class file; generated at runtime |
| Verbosity | High | Low |
Use a lambda whenever it fits. Reach for an anonymous class when you need to extend a class, implement several methods, hold state, or refer to the object itself with this.
Common Uses
Event listeners in GUI code (Swing, JavaFX, Android) historically used anonymous classes, especially when a listener interface has several methods:
window.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
saveDraft();
System.exit(0);
}
});
WindowAdapter is an abstract class with empty implementations of every WindowListener method, so the anonymous subclass overrides only the one it needs.
Adapting an abstract class once, as in the Task example above.
Test doubles: a quick fake implementation of an interface inside a test:
PaymentGateway alwaysDeclines = new PaymentGateway() {
@Override public boolean charge(String card, double amount) { return false; }
@Override public void refund(String txId) { }
};
Limitations and Cautions
- No constructors. Initialization uses superclass constructor arguments, field initializers, or an instance initializer block.
- One instance per evaluation. Each time the expression runs, a new object is created; the class itself cannot be reused elsewhere by name.
- Hidden outer reference. An anonymous class created in an instance method keeps the enclosing object alive. A long-lived anonymous listener can therefore leak its outer object, exactly like an inner class.
- Readability. Long anonymous classes nested inside method calls are hard to read. If the body grows beyond a few lines or is needed in two places, turn it into a named class.
Double-brace initialization: avoid it
You may see this idiom in older code:
List<String> list = new ArrayList<>() {{
add("a");
add("b");
}};
It creates an anonymous subclass of ArrayList with an instance initializer block. It works, but it creates an extra class for every use, captures the enclosing object (risking leaks), and can break equals and serialization. Use List.of("a", "b") or new ArrayList<>(List.of("a", "b")) instead.
Practical Example: Validation Rules
import java.util.ArrayList;
import java.util.List;
public class SignupValidation {
interface Rule {
String name();
boolean test(String value);
default String message() { return name() + " failed"; }
}
static Rule minLength(int n) {
return new Rule() {
public String name() { return "min length " + n; }
public boolean test(String v) { return v.length() >= n; }
};
}
static Rule notIn(List<String> banned) {
return new Rule() {
private int rejections = 0; // per-rule state
public String name() { return "not a common password"; }
public boolean test(String v) {
boolean ok = !banned.contains(v.toLowerCase());
if (!ok) rejections++;
return ok;
}
@Override
public String message() {
return "too common (rejected " + rejections + " time(s))";
}
};
}
public static void main(String[] args) {
List<Rule> rules = List.of(
minLength(8),
notIn(List.of("password", "12345678", "qwerty123")),
new Rule() {
public String name() { return "contains a digit"; }
public boolean test(String v) { return v.chars().anyMatch(Character::isDigit); }
}
);
for (String candidate : List.of("abc", "Password", "Gocourse2026")) {
List<String> problems = new ArrayList<>();
for (Rule r : rules) {
if (!r.test(candidate)) problems.add(r.message());
}
System.out.println(candidate + " -> " + (problems.isEmpty() ? "OK" : problems));
}
}
}
Output:
abc -> [min length 8 failed, contains a digit failed]
Password -> [too common (rejected 1 time(s)), contains a digit failed]
Gocourse2026 -> OK
Rule has three methods (one with a default), so it is not a functional interface and cannot be implemented by a lambda. Each anonymous class supplies name and test, capturing the method parameter (n or banned) that configures it. The notIn rule keeps a private counter and overrides the default message(), things a lambda could not do. The third rule is written inline because it is used only in this one place.