【问题标题】:functional Java-8 lambda vs. explicit expression reports an compiler error函数式 Java-8 lambda 与显式表达式报告编译器错误
【发布时间】:2013-10-15 10:15:56
【问题描述】:

现在我使用 java-8 将一些显式声明转换为 lambda 表达式并得到编译器错误。因此怀疑这是当前 java-8 版本 (b105) 的“错误”。

示例代码定义了两个使用和不使用 lambda 表达式的 Function 对象。两者都依赖于这些函数使用的谓词。虽然传统的实现可以工作,但 lambda 版本会报错:

java: 变量 fileExists 可能没有被初始化

这不是完全错误的,但是如果使用函数而不是创建函数本身,则谓词是相关的(因为显式版本运行良好)。 我应该报告错误(有人有链接吗?)还是我错过了什么?

public class FileOpener {

public FileOpener(Predicate<File> fileExists) {
    this.fileExists = fileExists;
}

final Predicate<File> fileExists;

final Function<File, FileInputStream> openLambda = file -> {
    try {
        return fileExists.test(file) ? new FileInputStream(file) : null;
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    }
};

// this version compiles
final Function<File, FileInputStream> openFunction = new Function<File, FileInputStream>() {
    @Override
    public FileInputStream apply(File file) {
        try {
            return fileExists.test(file) ? new FileInputStream(file) : null;
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
};

}

【问题讨论】:

  • 在报告错误之前,我会 (a) 升级到最新版本并 (b) 在专用邮件列表中询问,也许是这个:mail.openjdk.java.net/mailman/listinfo/jdk8-dev lambda 的行为似乎是合理的,因为它是一个捕获 lambda,它可能需要在构造时知道它的参数值。
  • fileExists 在哪里初始化?字段不是在构造函数之前初始化的吗?
  • @assylias:“捕获”仅适用于局部变量。由于它不在方法中,因此没有局部变量。
  • @Edwin:fileExists 在构造函数中初始化,因此在 openLambda 之后。问题是它是否像 java8 报告的那样是一个严重的问题。
  • @Ditz 问题是openLambda 是一个实例字段,因此,在对象创建期间,它在构造函数之前被评估,因此fileExists 那时没有初始化。

标签: java lambda java-8


【解决方案1】:

正如 Edwin Dalorzo 所指出的,错误消息是正确的。初始化顺序在这里很重要。首先字段初始值设定项按在源文件中出现的顺序执行,然后是构造函数。 Lambda 引用变量捕获变量,如果是最终变量(和所有局部变量),它们捕获需要初始化变量的变量的实际值。这符合 Java 语言规范,在 Java 8 中没有改变:

class SimpleTest
{
  SimpleTest()
  {
    first="a string";
  }
  final String first;
  String second=first;
}

在 Java 8 之前的版本中显示完全相同的行为。

【讨论】:

  • 补救办法是在构造函数中也初始化 openLambda --- 没有理由不这样做,只是它看起来有点笨拙。
  • 没错。另一种方法是删除 final 修饰符。但我总是更喜欢保留 final 修饰符并在构造函数中初始化。
  • 我认为这个答案不正确。 Lambda 仅“捕获”局部变量。实例变量通过外部类引用访问,不会被捕获。
  • @Holger:你有什么文件说 lambdas 可以捕获实例变量吗?
  • @user102008:您似乎将“捕获”误认为“复制”。 Lambda 肯定会捕获 成员变量,否则你无法访问它们。但是对于非final 实例变量,“捕获”意味着通过记住的this 实例访问它们。但是,lambdas 是否复制 final 变量的值或重新访问它们是一个不重要的实现特定细节;这些变量必须在创建 lambda 时明确分配,之后不得更改。
【解决方案2】:

我已经尝试过您的代码并玩了一下... 结果是,如果您从 fileExists 中删除 final 修饰符,一切都可以编译并且工作正常,因此 cmets 中的讨论可能不是 100% 准确的, fileExists 实际上可以在构造函数中初始化。看起来编译器没有检测到初始化实际上正在发生(嗯?)。

这可能是一个错误...我发现的唯一错误是http://bugreport.sun.com/bugreport/(尽管名义上是 SUN,但实际上是指向 Oracle 站点)。

P.S.:我使用的是 10 月 10 日的 b111。

【讨论】:

    猜你喜欢
    • 2017-01-06
    • 1970-01-01
    • 1970-01-01
    • 2017-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-25
    • 2012-08-01
    相关资源
    最近更新 更多