Variable capture in Lambda

无人久伴 提交于 2021-02-16 15:44:04

问题


I can't think why the captured variables are final or effectively final in lambda expressions. I looked over this question and really quite didn't get the answer.

What is this variable capture?

As I searched solutions for my problem, I read that these variables are final because of concurrency problems. But for such situation why can't we lock the task code in the lambda with a reentrant lock object.

public class Lambda {

  private int instance=0;

  public void m(int i,String s,Integer integer,Employee employee) {

    ActionListener actionListener = (event) -> {
      System.out.println(i);
      System.out.println(s);
      System.out.println(integer);
      System.out.println(employee.getI());
      this.instance++;
      employee.setI(4);
      integer++;//error
      s="fghj";//error
      i++;//error
    };
  }

}

In this particular code I want know the reasons why the last three statements gives an error, and why do we get to mutate Employee since it's a local variable.(Employee is just a class with getters and setters ofint i.)

Also i like to know why we can mutate this.instance too.

I appreciate a full detailed answer on all facts I mentioned above.


回答1:


I read that these variables are final because of concurrency problems.

Wrong, this has nothing to do with concurrency, it's all about how lambdas (and anonymous classes) "capture" variable values.

I want know the reasons why the last three statements gives an error

Because they are captures, so they must be effectively final.

You really don't need to know why the internals require this, just accept the fact that you need to adhere to that rule.

i like to know why we can mutate this.instance

Because the code doesn't capture instance, it captures this, and this is implicitly final.


Reason Why

A lambda is mostly syntactic sugar for an anonymous class. That's not really true, but for the purpose of this explanation, it's true enough, and the explanation is easier to understand for an anonymous class.

First understand, there is no such thing as an anonymous class in the JVM. Actually, there is no such thing as a lambda expression either, but that's a different story.

However, since Java (the language) has anonymous classes, but the JVM doesn't, the compiler has to fake it, by converting the anonymous class into an inner class. (FYI: Inner classes don't exist in the JVM either, so the compiler has to fake that too.)

Let's do this by example. Say we have this code:

// As anonymous class
int i = 0;
Runnable run = new Runnable() {
    @Override
    public void run() {
        System.out.println(i);
    }
}

// As lambda expression:
int i = 0;
Runnable run = () -> System.out.println(i);

For the anonymous class, the compiler will generate a class like this:

final class Anon_1 implements Runnable {
    private final int i;
    Anon_1(int i) {
        this.i = i;
    }
    @Override
    public void run() {
        System.out.println(i);
    }
}

and then compile the code to:

int i = 0;
Runnable run = new Anon_1(i);

That's how capture works, by copying the value of the "captured" variable.

The variable isn't captured at all, the value is, because Java is pass-by-value in the constructor call.

Now you can argue, that there is no reason why i should be effectively final. Sure, the local variable i and the field i are now separate, but they could be separately modified.

But there is a reason, and it's a really good reason. The fact that i has been copied, and is separate, is entire hidden, and is an implementation detail. Programmers would constantly forget that, and think they are the same, which would lead to lots of failed code, and many wasted hours of debugging to be reminded of that.

For code clarity, it must be as-if the i local variable was captured, and that the i in the anonymous class is the same as the i outside, because that is what the Java language defines it to be, even though the JVM can't do that.

To make it appear like that, the local variable MUST be effectively final, so the fact that (internally) the variable wasn't captured at all, makes no difference to the running code.



来源:https://stackoverflow.com/questions/61075400/variable-capture-in-lambda

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!