Причина исключения java.lang.VerifyError: Неверный тип в стеке операндов
Приведенный ниже простой код 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
конструктор, исключение не придет.
Кто-нибудь может сказать причину этого?
2 ответа
Проблема возникает потому, что ваше лямбда-выражение не ссылается this
или член this
но член внешнего this
, Если бы вы написали класс B
лайк
public class B extends A {
int innerData;
public B() {
super((data1, type) -> innerData = 1);
}
}
компилятор отклонил его без каких-либо сомнений в доступе innerData
подразумевает доступ this
,
Суть внешнего экземпляра заключается в том, что это константа, которая доступна даже тогда, когда внутренний экземпляр еще не полностью создан. Так что принимать код корректно, но, к сожалению, компилятор генерирует код, который пытается получить доступ к внешнему экземпляру через неявное поле экземпляра внутреннего класса, поэтому лямбда-выражение требует экземпляр внутреннего класса и пытается использовать не полностью созданный внутренний Экземпляр класса выдает ошибку.
Легко показать, что код может быть скомпилирован правильно:
public class B extends A {
public B() {
this(TestJavaCodes.this);
}
private B(TestJavaCodes outer) {
super((data1, type) -> outer.parentData = 1);
}
}
с этим небольшим изменением лямбда-выражение ссылается на внешний экземпляр, не обращаясь к внутреннему экземпляру, и ошибки не возникает.
Похоже, такой код вообще не должен компилироваться. Я свернул твой код:
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, поэтому отправил новый отчет об ошибке через веб-форму. Если Java читают это, назначенный внутренний идентификатор проверки - JI-9021379.
Обновление: отчет об ошибке принят ( JDK-8129740)