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

Ben*_*aye 10 java instance java-8 functional-interface supplier

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

@FunctionalInterface
interface PersonInterface {
    String getName();
}
Run Code Online (Sandbox Code Playgroud)

而这个实现:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果查看这些线程12,我希望输出以下代码"Bob",而不抛出a,NullPointerException因为据我了解,在创建我的Supplier时,它捕获了Person实例。

Person p = new Person("Bob");
Supplier<String> f = p::getName;
p = null;
System.out.println(f.get());
Run Code Online (Sandbox Code Playgroud)

并正确输出 "Bob"

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

Person p = new Person("Bob");
Supplier<String> f = p::getName;
p.setName("Alice");
System.out.println(f.get());
Run Code Online (Sandbox Code Playgroud)

它实际上输出 "Alice"

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

编辑 在重新阅读其他线程并获得Eran的回答后,我用2 Persons指向同一实例编写了该位:

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());
Run Code Online (Sandbox Code Playgroud)

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

Era*_*ran 6

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

首先,它是方法参考,而不是lambda表达式。

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

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


Eug*_*ene 6

这有没有关系lambda表达式或方法引用的方式,它的这些结构的只是副作用,您正在使用。

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

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

static class Person {
    private String name;
    // constructor/getter/setter
}
Run Code Online (Sandbox Code Playgroud)

当您创建:时Supplier<String> f = p::getName;,您可以将其视为创建一个SupplierHolder以a Person作为输入并具有对其的方法引用的方法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()); 
Run Code Online (Sandbox Code Playgroud)

在第二个示例中,您具有:

Person p = new Person("Bob");
SupplierHolder sh = new SupplierHolder(p); 
p.setName("Alice");
Run Code Online (Sandbox Code Playgroud)

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

实际上,这并不完全相同,但我想证明了这一点。