While doing some basic lambda exercises, the output from an apparently identical anonymous inner class was giving me a different output than the lambda.
interface Supplier<T> {
T get(T t);
}
Scenario #1
Supplier<Integer> s1 = new Supplier<Integer>() {
@Override
public Integer get(Integer t) {
return t;
}
};
Supplier<Integer> s2 = t -> t;
System.out.println(s1.get(2));
System.out.println(s2.get(2));
Outputs 2 and 2. Nothing new here.
But when I do this:
Scenario #2
Supplier<Integer> s1 = new Supplier<Integer>() {
@Override
public Integer get(Integer t) {
return t++;
}
};
Supplier<Integer> s2 = t -> t++;
System.out.println(s1.get(2));
System.out.println(s2.get(2));
Outputs 2 and 3
QUESTION: Shouldn't both outputs be identical? Am I missing something?
For the sake of completeness: Scenario #3
Supplier<Integer> s1 = new Supplier<Integer>() {
@Override
public Integer get(Integer t) {
return ++t;
}
};
Supplier<Integer> s2 = t -> ++t;
System.out.println(s1.get(2));
System.out.println(s2.get(2));
Outputs 3 and 3. Nothing new here as well.
UPDATE: Still getting same output from 1.8.0-b132
UPDATE #2: Bug report: https://bugs.openjdk.java.net/browse/JDK-8038420
UPDATE #3: The bug has been fixed in javac, you should be able to obtain the same result now.
According to generated bytecode:
Lambda:
Anonymous class:
As you can see, in anonymous class after loading variable from local variable table (method parameter t) runtime store copy of parameter in another variable (astore_2) and then use this copy of parameter as returning value.
Lambda method doesn't make copy of parameter (load -> unbox -> add 1 -> box -> store -> load -> return).
UPDATE
It's definitely a javac bug.
I got source from http://hg.openjdk.java.net/jdk8u/jdk8u
Anonymous class and lambda converts to following intermediate representations:
In lambda generated method parameter marked as final, because LambdaToMethod translator marks all parameters as FINAL (according source code LambdaTranslationContext.translate(…):1899).
Then let expression builder checks variable flags and when if it’s final omits temporary variable generation (according source code Lower.abstractRval(…):2277), because modification considered to be prohibited.
Possible solutions:
Remove FINAL flag from local variable (LambdaTranslationContext.translate(…):1894) and parameter (LambdaTranslationContext.translate(…):1899) in lamda generated method:
I removed FINAL flag and got expected results on tests from: https://bugs.openjdk.java.net/browse/JDK-8038420