Java lambda 与匿名内部类有不同的变量要求

标签 java lambda anonymous-inner-class

我有一个匿名内部类和一个等效的 lambda。为什么 lambda 的变量初始化规则更严格,有没有比匿名内部类更干净的解决方案或在构造函数中初始化它?

import java.util.concurrent.Callable;

public class Immutable {
    private final int val;

    public Immutable(int val) { this.val = val; }

    // Works fine
    private final Callable<String> anonInnerGetValString = new Callable<String>() {    
        @Override
        public String call() throws Exception {
            return String.valueOf(val);
        }
    };

    // Doesn't compile; "Variable 'val' might not have been initialized"
    private final Callable<String> lambdaGetValString = () -> String.valueOf(val);
}

编辑:我确实遇到了一种解决方法:对 val 使用 getter。

最佳答案

关于lambda expression bodies的章节状态

Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).

The transparency of this (both explicit and implicit) in the body of a lambda expression - that is, treating it the same as in the surrounding context - allows more flexibility for implementations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution.

因此他们更加严格。

在这种情况下,周围的上下文是对字段的赋值,而当前的问题是对字段的访问,val,空白的final字段,在表达式的右侧。

Java 语言规范声明

Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by this) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator = (§15.26.1).

For every access of a local variable or blank final field x, x must be definitely assigned before the access, or a compile-time error occurs.

接着说

Let C be a class, and let V be a blank final non-static member field of C, declared in C. Then:

  • V is definitely unassigned (and moreover is not definitely assigned) before the leftmost instance initializer (§8.6) or instance variable initializer of C.

  • V is [un]assigned before an instance initializer or instance variable initializer of C other than the leftmost iff V is [un]assigned after the preceding instance initializer or instance variable initializer of C.

你的代码基本上是这样的

private final int val;
// leftmost instance variable initializer, val still unassigned 
private final Callable<String> anonInnerGetValString = ...
// still unassigned after preceding variable initializer
private final Callable<String> lambdaGetValString = ...

因此,当在 lambdaGetValString 的初始化表达式中访问 val 时,编译器会确定它未赋值。

关于Java lambda 与匿名内部类有不同的变量要求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38020110/

相关文章:

java - 是否可以像在 android 中一样为我的应用程序的 Activity 创建一个最近的屏幕

python - 使用 Python/numpy 过滤 CSV 数据

c# - SelectMany(x => x) 的目的是什么?

c++ - 函数参数包含完整的函数

java - 匿名内部类 - 表达式的非法开始

java - 比较包含 Singleton 实例的两个变量

java - 保持数据库中的数据与水平刻度同步

java - 如何公开位于 mule devkit 连接器中的 jax-ws?

Java匿名内部类调用封闭类型父类(super class)型方法

java - 特定于枚举常量的类主体是静态的还是非静态的?