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

Java 方法引用似乎并不总是捕获实例,java,java-8,instance,functional-interface,supplier,Java,Java 8,Instance,Functional Interface,Supplier,我知道在这个问题上有很多问题,即使是一个问题,但我仍然不能围绕着一件事动脑。考虑下面的函数接口< /强>: @FunctionalInterface interface PersonInterface { String getName(); } 这一实施: class Person implements PersonInterface { private String name; public Person(String name) { this.na

我知道在这个问题上有很多问题,即使是一个问题,但我仍然不能围绕着一件事动脑。考虑下面的<强>函数接口< /强>:

@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;
    }
}
如果我查看这些线程,我希望下面的代码输出
“Bob”
,而不是抛出
NullPointerException
,因为据我所知,在创建供应商时,它捕获了
Person
实例

Person p = new Person("Bob");
Supplier<String> f = p::getName;
p = null;
System.out.println(f.get());
它实际上输出了
“Alice”

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

编辑 在重新阅读其他线程和Eran的答案后,我用两个人指向同一个实例写下了这段文字:

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());
人员p1=新人员(“Bob”);
人p2=p1;
供应商f1=p1::getName;
供应商f2=p2::getName;
p1=零;
p2.设置名称(“Alice”);
System.out.println(f1.get());
System.out.println(f2.get());
现在我可以看到,它们都输出了
“Alice”
,即使p1为null,因此方法引用捕获了实例本身,而不是我错误假设的状态

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

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

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


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

这与lambda或方法引用无关,只是您使用的这些构造的副作用

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

static class SupplierHolder {
    private final Person p;
    // constructor/getter
}

static class Person {
    private String name;
    // constructor/getter/setter
}
创建时:
Supplier f=p::getName
,您可以将其视为创建一个
供应商持有人
,该持有人将
人员
作为输入,并具有对其
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
reference和
SupplierHolder
持有的引用在同一个实例上“动作”——它们指向同一个对象


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

我想我现在明白了,我刚刚有了一个妙不可言的时刻。我在我的问题中添加了一个我尝试过的编辑,这为我清除了它。谢谢谢谢,我想我现在明白了,我已经接受了Eran的答案,因为它让我明白了这一点。我认为如果你改变了这个人的名字(比如在你的代码块中)而没有得到更新后的值,那会很奇怪/容易出错
Person p = new Person("Bob");
SupplierHolder sh = new SupplierHolder(p); 
p.setName("Alice");