λ Lambdas & Functional Java · Intermediate

this in lambdas vs anonymous classes in Java

A lambda's this is the enclosing instance.

🧩 The mysterySame 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
    }
}
🔮 Predict it

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(); }
  1. Cat
  2. A lambda class name like Cat$$Lambda
  3. null
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
    }
};
🔮 Predict it

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(); }
  1. outer
  2. anon
  3. Compile 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
        }
    };
}
⚠️ The trap

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();   // ✓
💼 In the real world

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

  1. Lambda: this = the enclosing object
  2. Anonymous class: this = the anonymous object itself
  3. Outer instance from an anonymous class: Outer.this
  4. A lambda parameter can't shadow an existing local variable
🤯 Did you know?

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(); }
  1. Demo
  2. A lambda class name like Demo$$Lambda
  3. null
  4. 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"));
  1. 3
  2. 1
  3. Compile error
  4. 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.

Next: snap small functions together like Lego. Does f.andThen(g) run f first or g first?