0

Consider the following examples

Consumer<Long> f1 = new Consumer<>() {

    @Override
    public void accept(Long value) {
        if (value < 5) {
            this.accept(value + 1); //this refers to the anonymous context.
        }
    }

}

Consumer<Long> f2 = (value) -> {
    if (value < 5) {
        this.accept(value + 1); //this refers to outer context and the anonymous function is not referable. 
    }
};

It is seen that this is not provided for the lambda f2 but is given for the more explicitly written anonymous Consumer implementation f1.

Why is this so? What would be the language design difficulty in providing this for the lambda body?

Amrish Kumar
  • 308
  • 3
  • 12
  • 1
    The possible duplicate explains how `this` works in lambda, how lambda is not an inner class. The question is, what is the language design difficulty to provide `this` for lambda instance and why JLS doesn't provide it? Consider it as a Java internal question. – Amrish Kumar Nov 23 '19 at 10:52
  • 2
    It's not a difficulty. It's something that is undesirable, and that has thus been designed that way, on purpose, because lambdas have a single method and don't have state, thus making it useless to have a `this`. On the other hand, being able to reference the enclosing classes methods and fields is extremely useful. – JB Nizet Nov 23 '19 at 11:15

1 Answers1

2

The Java Language Specification 15.27.2 says:

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.

Community
  • 1
  • 1
Thilo
  • 257,207
  • 101
  • 511
  • 656
  • 1
    I just discovered that I could do `Consumer f2 = (v) -> { if (v < 5) f2.accept(v + 1); }` for recursive calls. I am not sure if this is recommended and would work in all contexts. – Amrish Kumar Nov 23 '19 at 11:34
  • 1
    @AmrishKumar are you sure that works? [I get an error](https://ideone.com/ig7QRk) about `f2` not being initialized (as a local variable) and a self reference as a member variable. (And even if it did work, that would absolutely not be recommendable). – Andy Turner Nov 23 '19 at 11:39
  • In JShell it splits the statement into 2 different variables internally as f2_ and f2 making it work without the self reference error. My bad, It doesn't work elsewhere. I should stop using JShell to test statements. – Amrish Kumar Nov 23 '19 at 11:45
  • 1
    @AndyTurner For class member variables it can be done this way `Consumer f1 = (v) -> { if (v<5) this.f1.accept(v+1); };` – Amrish Kumar Nov 23 '19 at 13:01