在 javac 1.8.0_77 中这个类不编译:
import java.util.function.*;
public class xx {
final Object obj;
final Supplier<Object> supplier1 = new Supplier<Object>() {
@Override
public Object get() {
return xx.this.obj;
}
};
final Supplier<Object> supplier2 = () -> { return this.obj; };
xx(Object obj) {
this.obj = obj;
}
}
这是错误:
xx.java:12: error: variable obj might not have been initialized
final Supplier<Object> supplier2 = () -> { return this.obj; };
^
1 error
问题:
- 根据 JLS,此错误的生成是否正确?
- 如果是这样,JLS 处理
@FunctionalInterface
背后的原因是什么?在这方面,lamba 实现 (supplier2
) 与其等效的内部类实现 (supplier1
) 有何不同?
编辑添加 (2022/9/21)
仅供引用,这是一个修复此问题的简单编译器补丁。在 Flow.AssignAnalyzer
中,它导致 lambda 在字段初始化方面被视为非构造函数方法(即忽略它们) :
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
index 20abb281211..7e77d594143 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
@@ -2820,30 +2909,33 @@ public class Flow {
@Override
public void visitLambda(JCLambda tree) {
final Bits prevUninits = new Bits(uninits);
final Bits prevInits = new Bits(inits);
int returnadrPrev = returnadr;
+ int firstadrPrev = firstadr;
int nextadrPrev = nextadr;
ListBuffer<PendingExit> prevPending = pendingExits;
try {
returnadr = nextadr;
+ firstadr = nextadr;
pendingExits = new ListBuffer<>();
for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
JCVariableDecl def = l.head;
scan(def);
inits.incl(def.sym.adr);
uninits.excl(def.sym.adr);
}
if (tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION) {
scanExpr(tree.body);
} else {
scan(tree.body);
}
}
finally {
returnadr = returnadrPrev;
uninits.assign(prevUninits);
inits.assign(prevInits);
pendingExits = prevPending;
+ firstadr = firstadrPrev;
nextadr = nextadrPrev;
}
}
最佳答案
浏览 JSR 335 中的 JLS 更改,这对我来说似乎是一个遗漏:
- 对空白最终字段的访问受 JLS Chapter 16 管制
- 有一个部分 "Definite Assignment and Anonymous Classes"这在
supplier1
的情况下要求错误。 - JSR 335 spec第 16 章只有一个边际变化,在本章中从未提及“lambda”或“方法引用”。
事实上,第 16 章中唯一的变化是(使用粗体字进行添加):
Throughout the rest of this chapter, we will, unless explicitly stated otherwise, write V to represent an in-scope (6.3) local variable or a blank final field
(for rules of definite assignment) or a blank final variable (for rules of definite unassignment).
归根结底,编译器在 lambda 情况下不提示似乎是正确的,但为了保持一致性,JLS 也应该被修改以涵盖这种情况。
编辑::OpenJDK 已经有一个 spec bug为此,在我们发言时正在提议更改。
关于javac - 为什么 Java lambda 在实例字段初始化方面与嵌套类的处理方式不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39881295/