javac - 为什么 Java lambda 在实例字段初始化方面与嵌套类的处理方式不同?

标签 javac

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

关于javac - 为什么 Java lambda 在实例字段初始化方面与嵌套类的处理方式不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39881295/

相关文章:

java - Bash 编译 Java 类和 JAR

java - JOptionPane.showInputDialog 找不到符号

java - 设置正确的类路径来编译和运行 Java 包?

java - 如何从 javac 插件获取 MemberSelectTree 中的表达式类型?

android - 构建 android 4.3 master 时出错

java - 无法在 "/usr/bin/java/bin/java"(-1) 找到可执行文件

java - 为什么 javac 不能编译 Java 1.5 代码以在 Java 1.4 JVM 上运行?

javac 无法识别 java 7

java - 用javac编译java类不起作用

java - 在 Linux 终端编译一个 Net beans 创建的项目