为了账号安全,请及时绑定邮箱和手机立即绑定

方法引用似乎并不总是捕获实例

方法引用似乎并不总是捕获实例

蝴蝶不菲 2023-01-05 17:25:37

我知道关于这个主题有很多问题,即使是最近的一个问题,但我仍然无法解决一件事。考虑以下功能接口:


@FunctionalInterface

interface PersonInterface {

    String getName();

}

而这个实现:


class Person implements PersonInterface {

    private String name;


    public Person(String name) {

        this.name = name;

    }


    public String getName() {

        return name;

    }


    public void setName(String name) {

        this.name = name;

    }

}

如果我查看这些线程1和2,我希望输出以下代码"Bob"而不是抛出 aNullPointerException因为据我所知,当我创建供应商时,它会捕获Person实例。


Person p = new Person("Bob");

Supplier<String> f = p::getName;

p = null;

System.out.println(f.get());

它正确输出"Bob"


现在我不明白的是为什么下面的代码也没有输出"Bob"?


Person p = new Person("Bob");

Supplier<String> f = p::getName;

p.setName("Alice");

System.out.println(f.get());

它实际上输出"Alice"


在我看来,在第一个示例中,lambda 在创建时捕获了 Person 对象的状态,并且在调用它时不会尝试重新评估它,而在第二种情况下,它似乎没有捕获它,但在调用时重新评估它。


编辑 在重新阅读其他线程和 Eran 的回答后,我用 2 个人指向同一个实例写了那个:


Person p1 = new Person("Bob");

Person p2 = p1;

Supplier<String> f1 = p1::getName;

Supplier<String> f2 = p2::getName;

p1 = null;

p2.setName("Alice");

System.out.println(f1.get());

System.out.println(f2.get());

现在我可以看到它们都输出"Alice",即使 p1 为空,因此方法引用捕获了实例本身,而不是我错误假设的状态。


查看完整描述

2 回答

?
桃花长相依

TA贡献1598条经验 获得超8个赞

在我看来,在第一个示例中,lambda 在创建时捕获了 Person 对象的状态,并且在调用它时不会尝试重新评估它,而在第二种情况下,它似乎没有捕获它,但在调用时重新评估它。

首先,它是一个方法引用,而不是 lambda 表达式。

在这两种情况下,对Person实例的引用都被方法引用捕获(这不是“Person 对象的状态”)。这意味着如果Person实例的状态发生变化,则执行功能接口的方法的结果可能会发生变化。

方法引用不会创建Person它捕获其引用的实例的副本。


查看完整回答
反对 回复 2023-01-05
?
侃侃无极

TA贡献1765条经验 获得超10个赞

这在某种程度上与 lambdas 或方法引用无关,它只是您正在使用的这些构造的副作用。


为了更简单的推理,您可以将其视为:


static class SupplierHolder {

    private final Person p;

    // constructor/getter

}


static class Person {

    private String name;

    // constructor/getter/setter

}

当您创建:Supplier<String> f = p::getName;时,您可以将其视为创建一个SupplierHolder以 aPerson作为输入并具有对其getName.


这就像做:


Person p = new Person("Bob");

SupplierHolder sh = new SupplierHolder(p);

p = null; // this has no effect on the reference that SupplierHolder holds

System.out.println(sh.getPerson().getName()); 

在你的第二个例子中,你有:


Person p = new Person("Bob");

SupplierHolder sh = new SupplierHolder(p); 

p.setName("Alice");

现在p引用和SupplierHolder持有的引用“作用于”同一个实例——它们指向同一个对象。


这在现实中并不完全相同,但我想证明了这一点。


查看完整回答
反对 回复 2023-01-05

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信