以下简单的java代码发送 java.lang.VerifyError:操作数堆栈上的错误类型异常
public class TestJavaCodes {
int parentData = 0;
public void init() {
A ob = new B();
}
public static void main(String[] args) {
TestJavaCodes testJavaCodes = new TestJavaCodes();
testJavaCodes.init();
}
public static class A {
public A(MyLambdaFunc lambdaFunc) {
}
}
public class B extends A {
public B() {
super((data1, type) -> {
parentData = 1;
});
}
}
@FunctionalInterface
public static interface MyLambdaFunc {
public void onData(String data, int type);
}
}
如果我删除了代码
parentData = 1
来自B
的构造函数,不会出现异常。
任何人都可以说明原因吗?
答案 0 :(得分:14)
似乎这些代码根本不应该编译。我最小化了你的代码:
public class CompilerBug {
int var = 0;
public static void main(String[] args) {
new CompilerBug().new Inner();
}
public class Inner {
public Inner(Runnable r) {}
public Inner() {
this(() -> {
var = 1;
});
}
}
}
javac 1.8.0.25,1.8.0.40和1.9b57编译没有问题。每个编译版本在启动时都会产生相同的输出:
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
CompilerBug$Inner.<init>(LCompilerBug;)V @3: invokedynamic
Reason:
Type uninitializedThis (current frame, stack[2]) is not assignable to 'CompilerBug$Inner'
Current Frame:
bci: @3
flags: { flagThisUninit }
locals: { uninitializedThis, 'CompilerBug' }
stack: { uninitializedThis, 'CompilerBug', uninitializedThis }
Bytecode:
0000000: 2a2b 2aba 0003 0000 b700 04b1
at CompilerBug.main(CompilerBug.java:5)
此代码不是由ECJ编译器编译的。它报告编译错误:
----------
1. ERROR in C:\projects\Test\src\CompilerBug.java (at line 12)
this(() -> {
^^^^^
Cannot refer to 'this' nor 'super' while explicitly invoking a constructor
----------
1 problem (1 error)
所以它看起来像是javac编译器中的一个错误:它应该返回一个编译错误(如ECJ)。
我在OpenJDK错误跟踪器中没有发现类似的错误,因此通过webform提交了一个新的错误报告。如果Java人员正在阅读此内容,则分配的内部审阅ID为JI-9021379。
更新:接受错误报告(JDK-8129740)
答案 1 :(得分:12)
问题出现是因为您的lambda表达式不引用this
或this
的成员,而是外部 this
的成员。你曾写过课程B
喜欢
public class B extends A {
int innerData;
public B() {
super((data1, type) -> innerData = 1);
}
}
编译器毫无疑问地拒绝了它,因为访问innerData
意味着访问this
。
关于外部实例的观点是它是一个常量,当内部实例尚未完全构造时它甚至可用。因此接受代码是正确的,但不幸的是编译器生成的代码试图通过内部类实例的隐式字段访问外部实例,因此lambda表达式需要内部类的实例并尝试使用未完全构造的内部class instance产生错误。
可以很容易地证明代码可以正确编译:
public class B extends A {
public B() {
this(TestJavaCodes.this);
}
private B(TestJavaCodes outer) {
super((data1, type) -> outer.parentData = 1);
}
}
通过这个小的更改,lambda表达式引用外部实例而不访问内部实例,并且不会出现错误。