引用相同 Java 方法但返回不同地址

Same Java Method Referenced But Different Address Returned

我两次引用相同的方法,但引用不同。看这个例子:

import java.util.function.Consumer;

public class MethodRefTest {
    public static void main(String[] args) {
        new MethodRefTest();
    }

    public MethodRefTest() {
        Consumer<Integer> a = this::method;
        System.out.println(a);
        Consumer<Integer> b = this::method;
        System.out.println(b);
    }

    public void method(Integer value) {

    }
}

输出为:

MethodRefTest$$Lambda/250421012@4c873330
MethodRefTest$$Lambda/295530567@776ec8df

方法引用只是匿名的语法糖吗类?如果不是,我必须做什么才能始终获得相同的方法参考? (除了在要使用的字段中存储一次引用。)

(应用:我认为方法引用是观察者实现的一种更漂亮的方式。但是每次使用不同的引用,一旦观察者被添加就不可能从观察者中删除它。)

Are method references nothing more than syntactic sugar for anonymous classes?

正确。它们不一定总是像那样重量级地实现,但从概念上讲它们就是这样。

If not, what do I have to do to always get the same method reference? (Aside from storing a reference once in a field to work with.)

将引用存储在字段中。这就是答案。 (对不起。)

你问,

Are method references nothing more than syntactic sugar for anonymous classes?

JLS 说

Evaluation of a method reference expression produces an instance of a functional interface type

(JLS 8, section 15.13)

它没有明确要求匿名 class,但它确实需要 一些 class,并且它没有提供一种命名机制class。我可以想象替代方案,但使用现有的匿名 class 机制似乎很自然。

一个实现可以识别对同一方法的多个引用并为它们使用相同的匿名 class 是合理的,但这种行为绝不是必需的,并且您已经证明您的实现没有做吧。然而,即使实现确实这样做了,JLS 至少 建议 方法引用表达式的每次求值都会产生一个新对象。

你继续,

If not, what do I have to do to always get the same method reference? (Aside from storing a reference once in a field to work with.)

你唯一有保证的机​​制是只评估一次方法引用,然后只要你需要它就坚持对结果对象的引用。正如@JohnKugelman 所描述的那样,将引用存储在字段中是一种变体,但根据您需要引用同一方法引用对象的范围,将其存储在局部变量中或传递它可能就足够了通过方法参数。

一般来说,最简单有效的方法是将引用存储在字段或(本地)变量中。

首先,为方法引用生成的对象的 toString() 输出是完全未指定的,因此,您不能从那里得出关于对象身份的任何结论。此外,十六进制数是一个依赖于实现的哈希码,很少是地址。检查对象身份的唯一可靠方法是 a==b.

尽管如此,这些对象确实不同,但这是实现细节。正如 Does a lambda expression create an object on the heap every time it's executed? 中所解释的,JVM 在对象重用方面有很大的自由度,但是当前的 HotSpot/OpenJDK 实现将只对非捕获表达式重用对象,而 this::method 正在捕获 this参考。此外,如您问题的代码所示,代码中每次出现 this::method 甚至都会生成自己的 class.

这在 Is method reference caching a good idea in Java 8? 中进行了解释,其中还得出结论,出于性能原因,您不应保留这些实例。但是在您的情况下,当您想要可靠地注销侦听器时,将引用保留在变量中是 only 的方法。即使对于非捕获表达式的单次出现,当前实现将为其提供相同的对象,也不能保证这会起作用,因为这种重用仍然是一种依赖于实现的行为。