操作数堆栈上的错误类型…使用jdk 8,带有匿名内部类的lambdas失败,为什么?
运行下面的代码会导致Bad type on operand stack
的错误消息Bad type on operand stack
。
public static void main(String args[]) { TransformService transformService = (inputs) -> { return new ArrayList(3) {{ add("one"); add("two"); add("three"); }}; }; Collection inputs = new HashSet(2) {{ add(5); add(7); }}; Collection results = transformService.transform(inputs); System.out.println(results.size()); } public interface TransformService { Collection transform(Collection inputs); }
但是,删除lamda中的双括号初始化(匿名内部类)允许代码按预期运行,为什么? 以下作品:
public class SecondLambda { public static void main(String args[]) { TransformService transformService = (inputs) -> { Collection results = new ArrayList(3); results.add("one"); results.add("two"); results.add("three"); return results; }; Collection inputs = new HashSet(2) {{ add(5); add(7); }}; Collection results = transformService.transform(inputs); System.out.println(results.size()); } public interface TransformService { Collection transform(Collection inputs); } }
编译错误? 毕竟这是早期的访问版本……
(除非你有最新的jdk 8 lambda下载,否则不会编译。)
看来,这个问题不仅发生在lambda
返回anonymous
类型的情况下,而且即使在lambda
构造了任何匿名类。 即:
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(); } }
这实际上导致Exception in thread "main" java.lang.VerifyError: Bad local variable type
(…) Reason: Type top (current frame, locals[0]) is not assignable to reference type
。
进一步调查表明,如果我们将参数引入方法xxx
,exception的原因将包含其类型。 例如:
Type 'java/lang/Integer' (current frame, stack[0]) is not assignable to 'lambda/TestLambda'
这已经非常有趣了。 让我们将xxx
参数的类型(实际上并未使用)更改为顶级类型,即TestLambda
:
... xxx(new TestLambda()); } private static void xxx(TestLambda x) { ...
你觉得怎么样? 这解决了这个问题! 一切都开始好起来。 甚至,如果我们改变return A();
return new A() {};
。 检查一下!
我的结论是,这是真正的JVM错误 。 看来,问题在于加载类的堆栈。 它与方法相结合, Java
用于翻译lambda
表达式( http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html ) – 它在顶级类中生成合成方法。 看来,当lambda
堆栈中引入匿名类时会被破坏。 它可以使用上面提到的解决方法来修复。
编译错误? 毕竟这是早期的访问版本……
我会说任何提到操作数堆栈的错误消息很可能是由于编译器错误或JVM中的错误。 特别是如果你可以使用纯Java示例来获取它。
(看起来JVM正在报告编译器应该检测到的类型安全问题,和/或类加载时的字节码validation程序。)
通过推荐的Java 8错误频道报告。
与您的问题没有直接关系,但我强烈建议您不要以这种方式使用匿名类。 您只是为了向其添加两个值而创建一个全新的HashSet子类型。 这不仅会使系统膨胀(它永远留在内存中),它也会混淆JVM的JIT,因为它永远不会在呼叫站点看到HashSet ……它会看到你创建的许多子类型之一。