this in lambdas vs anonymous classes in Java
A lambda's this is the enclosing instance.
println(this), same class. Inside a lambda it prints "outer". Inside an anonymous class it prints "inner". Two ways of writing a Runnable, two different worlds.Lambdas don't open a new room
A lambda is lexically scoped: names inside it mean exactly what they mean in the surrounding code. So **this inside a lambda is the enclosing instance**, not the lambda.
class Demo {
void go() {
Runnable r = () -> IO.println(this);
// this = the Demo object
}
}Your turn
What does this print?
class Cat {
public String toString() { return "Cat"; }
void go() {
Runnable r = () -> IO.println(this);
r.run();
}
}
void main() { new Cat().go(); }CatA lambda class name like Cat$$Lambdanull
Show the answer
this inside the lambda is the Cat object that called go(), so its toString() prints Cat.
Anonymous classes are real objects
An anonymous class creates a brand-new object, with its own this. Inside it, this refers to that anonymous object, not the surrounding one.
Runnable r = new Runnable() {
public void run() {
IO.println(this); // anonymous obj
}
};Inner or outer?
What does this print?
class Demo {
public String toString() { return "outer"; }
Runnable r = new Runnable() {
public String toString() {
return "anon";
}
public void run() { IO.println(this); }
};
}
void main() { new Demo().r.run(); }outeranonCompile error
Show the answer
The anonymous class is a new object, and its own toString returns "anon". A lambda in the same spot would have printed "outer".
Reaching outside: Outer.this
From inside an anonymous class, the enclosing object is the qualified this: **Demo.this**. Plain this would mean the anonymous object.
class Demo {
Runnable r = new Runnable() {
public void run() {
IO.println(Demo.this); // outer
}
};
}No shadowing in lambdas
Because a lambda doesn't open a new scope, its parameter can't reuse the name of a local variable that's already in scope. Anonymous class methods could; lambdas can't.
String s = "x";
Function<String, Integer> f =
s -> s.length(); // ✗ s is already defined
Function<String, Integer> g =
t -> t.length(); // ✓Listeners and callbacks
Old Swing and Android code is full of anonymous listeners with MyActivity.this sprinkled everywhere. Converting them to lambdas removes that noise, but double-check any this inside: its meaning changes during the conversion.
Key takeaways
- Lambda: this = the enclosing object
- Anonymous class: this = the anonymous object itself
- Outer instance from an anonymous class: Outer.this
- A lambda parameter can't shadow an existing local variable
Anonymous classes compile to their own .class files, like Demo$1.class. Lambdas don't: their bodies become private methods of the enclosing class, and the function object is created at runtime.
Practice questions
What does this print?
class Demo {
public String toString() { return "Demo"; }
void go() {
Runnable r = () -> System.out.println(this);
r.run();
}
}
void main() { new Demo().go(); }- Demo
- A lambda class name like Demo$$Lambda
- null
- Compile error
Check your answer
Demo. this inside the lambda is the Demo object that called go(), so its toString() prints "Demo".
What does this print?
String s = "x";
Function<String, Integer> f = s -> s.length();
System.out.println(f.apply("abc"));- 3
- 1
- Compile error
- Throws IllegalStateException
Check your answer
Compile error. A lambda doesn't open a new scope for names, so its parameter s clashes with the local variable s that's already defined.