When a lambda captures a variable, you can think of it as the lambda getting a copy of that variable's value. Because only final or effectively final variables can be captured, it is safe for lambdas to copy them. Their values won't change, so there's no danger of the copy being out of sync with the original variable.
Lambdas needn't be implemented with anonymous classes, but you can think of them conceptually as syntactic sugar for anonymous classes. Your whenComplete
call is equivalent to:
long startTime = System.currentTimeMillis();
result.whenComplete(new BiConsumer<T, U>() {
@Override public void accept(T res, U err) {
System.out.println(System.currentTimeMillis() - startTime);
}
});
The thing is, variable capturing isn't new with lambdas. Anonymous classes also capture variables, and they did it before lambdas came along. What happens is, they secretly stash away copies of captured variables. They get synthetic private fields to store those stashed values. The code above actually is more like this, if we make the synthetic field explicit:
long startTime = System.currentTimeMillis();
result.whenComplete(new BiConsumer<T, U>() {
private final long _startTime = startTime;
@Override public void accept(T res, U err) {
System.out.println(System.currentTimeMillis() - _startTime);
}
});
Notice that once this anonymous BiConsumer
is instantiated it stands on its own two feet. The body of accept()
now refers to an instance variable, not to the captured variable. The anonymous object is not tied to the outer function, nor to the thread in which it was created. accept()
can be called at any time and from any thread and will behave as one would expect, even if the original startTime
variable is long since dead and buried.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…