【问题标题】:How does java lambdas external references work?java lambdas 外部引用是如何工作的?
【发布时间】:2020-02-27 23:11:59
【问题描述】:

我想知道 lambdas 外部引用是如何工作的。让我解释一下:

假设我有这个供应商实现和这个模型类:

public class TestSupplierImpl implements Supplier<Boolean> {

    public Predicate<Integer> predicate;
    public TestSupplierModel model;

    public TestSupplierImpl() {
        this.predicate = i -> model.something.equals(i);
    }

    @Override
    public Boolean get() {
        return predicate.test(3);
    }
}


    class TestSupplierModel {
        public Integer something;

        public TestSupplierModel(Integer something) {
            this.something = something;
        }
    }

然后我执行以下代码:

    TestSupplierImpl test = new TestSupplierImpl(); // line 1
    test.model = new TestSupplierModel(3); // line 2
    Boolean resultado = test.get(); // line 3

第 1 行:创建 TestSupplierImpl 的新实例。这个新实例的谓词具有 model 的空引用。这是有道理的,因为在创建谓词时,模型引用为空。 第 2 行:为变量 model 分配一个新的 TestSupplierModel 实例。 第 3 行:test.predicate 现在具有带有新分配值的 model 引用。 这是为什么呢?

我不明白为什么,当我更改 model 引用时,谓词会将其模型引用更新为新的。怎么样?

提前致谢!

【问题讨论】:

  • 因为 lambda 捕获了this(即TestSupplierImpl的实例),而model实际上是指TestSupplierImpl.this.model

标签: java lambda


【解决方案1】:

如果你重写你的TestSupplierImpl()构造函数是否有意义?


    public Predicate<Integer> predicate;
    public TestSupplierModel model;

    public TestSupplierImpl() {
        // same effect as this.predicate = i -> model.something.equals(i);
        this.predicate = new Predicate<Integer>() {
            public boolean test(Integer i) {
                return model.something.equals(i);
            }
        };
        
    }

    @Override
    public Boolean get() {
        return predicate.test(3);
    }

所以这是事情的顺序。

        // the constructor is run and the test method defined BUT NOT executed.
        TestSupplierImpl test = new TestSupplierImpl(); // line 1
       
        // Now you define model
        test.model = new TestSupplierModel(3); // line 2
        
        // Then you execute the predictate via get()
        Boolean resultado = test.get(); // line 3


在您发出get() 方法之前,不需要model and something。到那时,它们已经被定义了。

【讨论】:

  • 谢谢你的回答,我明白了:)。我不明白的是为什么 lambdas 外部引用有时必须是最终的/实际上是最终的,而在其他地方(上面示例中的链接)它不需要是最终的。
  • 这与并发问题有关。局部变量存储在堆栈中,而实例字段存储在堆中。如果一个线程要从堆栈上的 lambda 访问一个值,它可能正在访问一个不再在范围内或被取消分配的值。如果它们实际上是最终的,则它们无法更改,因此它本质上是一个副本。相似之处在于,局部变量不受线程内交互的影响,而实例字段则不然。
  • 太好了,我写了另一个例子,我想通了。感谢WJS的解释!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多