12

I am little bit confused with the different behavior of an anonymous class and a lambda expression.

When I'm using a lambda expression:

//Test.java

Runnable r1 = () -> System.out.println(this);
Runnable r2 = () -> System.out.println(toString());

@Override
public String toString() {
    return "Hello World!";  
}

// in main method 
new Test().r1.run();
new Test().r2.run();   
Output : Hello World!
         Hello World!

When using an anonymous class:

Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println(this);   
    }
};

Runnable r2 = new Runnable() {
    @Override
    public void run() {
        System.out.println(toString()); 
    }
};

@Override
public String toString() {
    return "Hello World!";  
}

// in main method 
new Test().r1.run();
new Test().r2.run();  
Output : Package_Name.Test$1@1db9742
         Package_Name.Test$2@106d69c

Can someone please explain the different behavior?

4castle
  • 32,613
  • 11
  • 69
  • 106
Mehraj Malik
  • 14,872
  • 15
  • 58
  • 85

1 Answers1

14

In a lambda expression, this is lexically bound to the surrounding class, while in the anonymous class this is lexically bound to the anonymous class.

The Java Language Specification describes this behavior at 15.27.2:

Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).

The transparency of this (both explicit and implicit) in the body of a lambda expression - that is, treating it the same as in the surrounding context - allows more flexibility for implementations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution.

Practically speaking, it is unusual for a lambda expression to need to talk about itself (either to call itself recursively or to invoke its other methods), while it is more common to want to use names to refer to things in the enclosing class that would otherwise be shadowed (this, toString()). If it is necessary for a lambda expression to refer to itself (as if via this), a method reference or an anonymous inner class should be used instead.

In order to reference this of the surrounding class from inside an anonymous class, you will have to use a qualified this.

Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println(Test.this); // or Test.this.toString()
    }
};
Community
  • 1
  • 1
4castle
  • 32,613
  • 11
  • 69
  • 106
  • 1
    Thanks for the answer :) , however what is lexically bound? – Mehraj Malik Feb 27 '17 at 06:48
  • 2
    @MehrajMalik It means they are bound at compile time based on the context they were found in. – 4castle Feb 27 '17 at 06:50
  • 4
    This does not only affect `this` and `super`. Inside an anonymous inner class, there might be inherited members (not only `toString()`) whereas a lambda expression does not inherit anything from the functional interface. – Holger Feb 27 '17 at 10:10
  • for lexical scoping, please see this amazing video : https://www.youtube.com/watch?v=dHYhMP8ESuk – Mehraj Malik Nov 15 '20 at 06:17