【问题标题】:Java lambdas have different variable requirements than anonymous inner classesJava lambda 与匿名内部类有不同的变量要求
【发布时间】:2016-10-27 11:16:09
【问题描述】:

我有一个匿名内部类和一个等效的 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 使用吸气剂。

【问题讨论】:

  • 有趣的是,如果你删除 val 上的 final 修饰符,它会编译...
  • 当您将String.valueOf(val) 更改为String.valueOf(Immutable.this.val) 时,它也会编译
  • @csharpfolk this 代替 Immutable.this 也可以。
  • @lucasvw 我正在使用这个“在线 java ide”站点:compilejava.net,但它仅适用于 this

标签: java lambda anonymous-inner-class


【解决方案1】:

关于lambda expression bodies的章节

与匿名类声明中出现的代码不同,的含义 名称以及出现在 lambda 正文中的 thissuper 关键字, 与引用声明的可访问性一起,是相同的 和周围的上下文一样(除了 lambda 参数引入 新名称)。

this(显式和隐式)在 a 的主体中的透明度 lambda 表达式 - 也就是说,将其视为与 周围的上下文 - 为实现提供更大的灵活性,并且 防止正文中不合格名称的含义 依赖于重载决议。

因此他们更加严格。

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

Java 语言规范声明

每个局部变量 (§14.4) 和每个空白 final 字段 (§4.12.4, §8.3.1.2) 必须有一个明确分配的值,当它的任何访问 值发生。

对其值的访问由变量的简单名称组成 (或者,对于一个字段,由this 限定的字段的简单名称) 出现在表达式中的任何位置,除了作为的左操作数 简单的赋值运算符 = (§15.26.1)。

对于局部变量或空白final 字段x 的每次访问,x 必须是 肯定是在访问之前赋值的,否则会发生编译时错误。

接着说

C是一个类,让V是一个空白finalstatic成员字段 C,在 C 中声明。那么:

  • V 在最左边的实例初始化程序(第 8.6 节)或实例变量之前肯定是未分配的(而且也不是绝对分配的) C 的初始化器。

  • VC 的实例初始化程序或实例变量初始化程序之前 [un] 分配,除了最左边的当且仅当 V 是 [un] 在前面的实例初始化器或实例之后赋值 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 时,编译器会确定它未赋值。

上述规则适用于使用简单名称val,不适用于限定表达式this.val。你可以使用

final Callable<String> lambdaGetValString = () -> String.valueOf(this.val);

【讨论】:

    【解决方案2】:

    这不会编译:

    public class Example
    {
      private final int x;
      private final int y = 2 * x;
    
      public Example() {
        x = 10;
      }
    }
    

    但这会:

    public class Example
    {
      private final int x;
      private final int y;
    
      public Example() {
        x = 10;
        y = 2 * x;
      }
    }
    

    这也是这样:

    public class Example
    {
      private final int x = 10;
      private final int y = 2 * x;
    }
    

    所以它与 lambdas 无关。 在执行构造函数之前评估在声明它的同一行上初始化的字段。 所以此时,变量“val”(或本例中的“x”)尚未初始化。

    【讨论】:

    • 定义同一行
    【解决方案3】:

    在我的例子中,我有一个 Predicate 试图访问一个 private final 实例变量。我也做了Predicate 最终修复它。

    之前 - 编译器错误,this.availableCities 可能尚未初始化

    class Service {
      private final List<String> availableCities;
      Service(List<String> availableCities) {
        this.availableCities = availableCities;
      }
      private Predicate<String> isCityAvailable = city -> this.availableCities.contains(city);
    }
    

    之后 - 不再出现错误

    class Service {
      private final List<String> availableCities;
      private final Predicate<String> isCityAvailable;
      Service(List<String> availableCities) {
        this.availableCities = availableCities;
        this.isCityAvailable = city -> this.availableCities.contains(city);
      }
    }
    

    我认为“可能尚未初始化”编译器错误有一些优点,否则没有什么能阻止您在最终实例变量初始化之前从构造函数中调用 Predicate

    编译器强制执行此操作的可能原因

    class Service {
      private final List<String> availableCities;
      Service(List<String> availableCities, String topCity) {
        boolean isTopCityAvailable = isCityAvailable.test(topCity); // Error: this.availableCities is not initialized yet
        this.availableCities = availableCities;
      }
      private Predicate<String> isCityAvailable = city -> this.availableCities.contains(city);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多