Running the code below results in the error message Bad type on operand stack
.
public static void main(String args[]) {
TransformService transformService = (inputs) -> {
return new ArrayList<String>(3) {{
add("one");
add("two");
add("three");
}};
};
Collection<Integer> inputs = new HashSet<Integer>(2) {{
add(5);
add(7);
}};
Collection<String> results = transformService.transform(inputs);
System.out.println(results.size());
}
public interface TransformService {
Collection<String> transform(Collection<Integer> inputs);
}
However removing the double brace initialization (anonymous inner classes) within the lamda allows the code to run as expected, why ? The below works :
public class SecondLambda {
public static void main(String args[]) {
TransformService transformService = (inputs) -> {
Collection<String> results = new ArrayList<String>(3);
results.add("one");
results.add("two");
results.add("three");
return results;
};
Collection<Integer> inputs = new HashSet<Integer>(2) {{
add(5);
add(7);
}};
Collection<String> results = transformService.transform(inputs);
System.out.println(results.size());
}
public interface TransformService {
Collection<String> transform(Collection<Integer> inputs);
}
}
Compiler bug ? It is the early access version after all ...
(This won't compile unless you have the latest jdk 8 lambda download.)
It seems, that problem occurs not only in case when lambda
returns anonymous
type, but even if any anonymous class is constructed inside lambda
. I.e.:
public class TestLambda {
public static void main(String[] args) {
xxx();
}
static void xxx() {
Functional1 f = () -> {
Object o = new Object() { };
return new A();
};
}
static class A { }
static interface Functional1 { A func(); }
}
This actually leads to Exception in thread "main" java.lang.VerifyError: Bad local variable type
(...) Reason: Type top (current frame, locals[0]) is not assignable to reference type
.
Further investigation shows, that if we will introduce parameter into method xxx
, the reason for an exception will contains its type. E.g.:
Type 'java/lang/Integer' (current frame, stack[0]) is not assignable to 'lambda/TestLambda'
And this is already very interesting. Let's change type of xxx
parameter (which is not actually used) to type of top class, i.e. TestLambda
:
...
xxx(new TestLambda());
}
private static void xxx(TestLambda x) {
...
And what do you think? This fixes the problem! Everything begin work well. Even, if we will change return A();
to return new A() {};
. Check this!
My conclusion is that this is real JVM bug. It seems, that the problem is with stack of loaded classes. It occurs in combine with method, which Java
uses for translating lambda
expressions (http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html) - it produces synthetic methods inside top class. It seems, that when anonymous classes are introduced in lambda
stack becomes broken. It could be fixed using mentioned workaround.
Compiler bug ? It is the early access version after all ...
I would say that any error message that mentions the operand stack is highly likely to be a due to a compiler bug or a bug in the JVM. Especially if you can get it using a pure Java example.
(It looks like the JVM is reporting a typesafety problem that should have been detected by the compiler, and/or the bytecode verifier at class-load time.)
Report it via the recommended channel for Java 8 bugs.
Not directly related to your issue, but I'd strongly recommend not using anonymous classes in this way. You're creating an entirely new HashSet subtype solely for the purpose of adding two values to it. Not only does this bloat up the system (it stays in memory forever) it can also confound the JVM's JIT since it never just sees HashSet at a call site...it sees one of many subtypes you've created.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With