1. Diving into anonymous classes
An anonymous class is a nameless subclass or interface implementation created right at the point of use. Before lambdas (Java 8) it was the most convenient way to provide a “one-off” implementation of an interface or abstract class.
A classic:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello from an anonymous class!");
}
};
r.run();
Here we declared and immediately implemented the Runnable interface — without a separate file or a class name. Such implementations were often used for event handlers, comparators, threads, and other tasks where you need to quickly “plug in” behavior.
If a lambda is an “expression on the fly”, then an anonymous class is a “little actor without a name”: it plays a bit part and disappears.
2. Comparison with lambda expressions
Syntax
Anonymous class:
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};
Lambda expression:
Comparator<String> comp = (a, b) -> a.length() - b.length();
The difference is obvious: a lambda is more compact — you don’t need to spell out types, the method name, or extra braces if the action is simple.
Functionality
- Anonymous class — a full-fledged object. You can declare fields, extra methods, and override Object methods (toString, equals, etc.).
- Lambda expression — an implementation of a single abstract method of a functional interface. You cannot declare your own fields or additional methods inside it.
Which to choose when?
- Lambda — when you need a concise implementation of a single method of a functional interface.
- Anonymous class — when you need to:
- implement multiple methods (for example, of an abstract class);
- declare fields for state;
- override Object methods (for example, toString);
- leverage inheritance/access specifics (for example, access to protected members of a superclass).
3. Scope and the this keyword
Here lies a common pitfall:
- in an anonymous class this refers to the instance of the anonymous class;
- in a lambda expression this refers to the enclosing class in which the lambda is declared.
Example: compare behavior
public class Outer {
String name = "Outer class";
void test() {
Runnable anon = new Runnable() {
String name = "Anonymous class";
@Override
public void run() {
System.out.println(this.name); // "Anonymous class"
}
};
Runnable lambda = () -> System.out.println(this.name); // "Outer class"
anon.run();
lambda.run();
}
}
Output:
Anonymous class
Outer class
In an anonymous class, this points to the anonymous class itself (its name field is used). In a lambda, this is Outer.
4. When should you use anonymous classes?
If you need to implement more than one method
A lambda works only with functional interfaces (exactly one abstract method). If an interface/abstract class requires multiple methods — you need an anonymous class.
abstract class Animal {
abstract void say();
abstract void jump();
}
Animal cat = new Animal() {
@Override
void say() {
System.out.println("Meow!");
}
@Override
void jump() {
System.out.println("Jump!");
}
};
If you need to keep state (fields)
Runnable r = new Runnable() {
int counter = 0;
@Override
public void run() {
counter++;
System.out.println("Called " + counter + " time(s)");
}
};
r.run(); // Called 1 time(s)
r.run(); // Called 2 time(s)
If you need to override Object methods
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
@Override
public String toString() {
return "String length comparator";
}
};
System.out.println(comp); // String length comparator
5. Examples: Comparator and Runnable — lambda vs anonymous class
Sorting strings by length
Anonymous class:
List<String> words = Arrays.asList("cat", "elephant", "mouse", "tiger");
words.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
System.out.println(words);
Lambda expression:
List<String> words = Arrays.asList("cat", "elephant", "mouse", "tiger");
words.sort((a, b) -> a.length() - b.length());
System.out.println(words);
The result is the same, but the lambda code is shorter and easier to read.
Runnable: starting a thread
Anonymous class:
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread via anonymous class");
}
});
t1.start();
Lambda expression:
Thread t2 = new Thread(() -> System.out.println("Thread via lambda"));
t2.start();
Anonymous class with fields
Runnable r = new Runnable() {
int count = 0;
@Override
public void run() {
count++;
System.out.println("Called " + count + " time(s)");
}
};
r.run(); // Called 1 time(s)
r.run(); // Called 2 time(s)
You can’t do this in a lambda — there’s no way to declare a field.
6. Nuances: scope, variables, and final
In both anonymous classes and lambda expressions, you can use local variables from the enclosing method only if they are final or “effectively final” (not changed after initialization). But there’s a nuance with names:
- in an anonymous class, you can declare a variable with the same name as in the outer scope (“shadowing”);
- in a lambda — you cannot: the name must not conflict with an outer variable’s name.
Example:
int x = 10;
Runnable r = new Runnable() {
@Override
public void run() {
int x = 20; // OK: shadows the outer variable
System.out.println(x); // 20
}
};
r.run();
Runnable l = () -> {
// int x = 30; // Compilation error: variable already defined
System.out.println(x); // 10
};
l.run();
7. When is a lambda better, and when is an anonymous class indispensable?
Choose lambda expressions if:
- you need to implement a short function for a functional interface;
- you don’t need to keep state;
- you don’t need to override Object methods;
- the implementation is “here and now” and simple.
An anonymous class is necessary if:
- you must implement an interface with multiple methods or an abstract class;
- you need to declare fields or additional methods;
- you need to override toString, equals, hashCode;
- you need access to protected members of a superclass.
8. Practice: side-by-side comparison
Task 1: Filtering a list via Predicate
Anonymous class:
List<String> animals = Arrays.asList("cat", "elephant", "mouse", "tiger");
animals.removeIf(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length() < 4;
}
});
System.out.println(animals); // [elephant, mouse, tiger]
Lambda expression:
List<String> animals = Arrays.asList("cat", "elephant", "mouse", "tiger");
animals.removeIf(s -> s.length() < 4);
System.out.println(animals); // [elephant, mouse, tiger]
Task 2: Comparing the scope of this
public class Demo {
String name = "Demo";
void check() {
Runnable anon = new Runnable() {
String name = "Anon";
@Override
public void run() {
System.out.println(this.name); // "Anon"
}
};
Runnable lambda = () -> System.out.println(this.name); // "Demo"
anon.run();
lambda.run();
}
public static void main(String[] args) {
new Demo().check();
}
}
9. Common mistakes when working with anonymous classes and lambda expressions
Error #1: Expecting a lambda to implement multiple methods. A lambda works only with functional interfaces (one abstract method). If there are more methods — use an anonymous class.
Error #2: Confusion about the scope of this. In a lambda, this is the enclosing class; in an anonymous class, it’s the anonymous class itself. Because of this, it’s easy to end up with the “wrong” fields and values.
Error #3: Trying to declare fields in a lambda. You can’t declare your own fields in a lambda — you can only use variables from the outer context (final/“effectively final”). Use an anonymous class for state.
Error #4: Variable shadowing. In an anonymous class, you can declare a local variable with the same name as an outer one — that’s shadowing. In a lambda you can’t: the compiler will report an error.
Error #5: Logic that’s too complex for a lambda. If the lambda body grows beyond 3–5 lines, readability suffers. It’s better to extract the code into a separate method or use an anonymous class (if you need state/multiple methods).
GO TO FULL VERSION