From reading the JLS after a frustrating debugging session I find that lambdas will capture the value of effectively-final local variables, but if you refer to an instance variable it captures a reference to the variable, which has serious implications for multi-threaded code.
For example, the following is an MCVE distilled from a much larger program:
public class LambdaCapture
{
public static void main(String[] args) throws Exception
{
Launcher i1 = new Launcher();
i1.launchAsynchTask();
}
public static class Launcher
{
private int value = 10;
public void launchAsynchTask() throws Exception
{
System.out.printf("In launchAsynchTask value is %s\n",value);
Thread t = new Thread(()->doSomething(value));
t.start();
value = -1;
t.join();
}
public void doSomething(int value)
{
System.out.printf("In asynch task, value is %s\n",value);
}
}
}
I found the output surprising. It is
In launchAsynchTask value is 10
In asynch task, value is -1
since I initially (prior to JLS research) and intuitively expected the lambda to capture the value of the variable value instead of a reference to it.
If I have to guarantee that the current value is captured instead of a reference the obvious solution is to create a local final temporary:
final int capture = this.value;
Thread t = new Thread(()->doSomething(capture));
My question: Is this the accepted idiomatic way to force value capture, or is there some other more natural way to do it?