在 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 

问题:

  1. 根据 JLS,此错误的生成是否正确?
  2. 如果是这样,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 更改,这对我来说似乎是一个遗漏:

事实上,第 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为此,在我们发言时正在提议更改。


评论关闭
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!