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

时间:2017-10-06 19:31:03

标签: java method-reference functional-interface

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

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$1/250421012@4c873330
MethodRefTest$$Lambda$2/295530567@776ec8df

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

(应用程序:我认为方法引用是一种更漂亮的观察者实现方式。但是每次添加时都不可能从一个observable中删除一个观察者。)

4 个答案:

答案 0 :(得分:3)

  

方法引用只不过是匿名类的语法糖吗?

正确。它们并不一定总是被实施为重量级,但从概念上讲它们就是全部。

  

如果没有,我需要做什么才能始终获得相同的方法参考? (除了在字段中存储一次引用以便使用。)

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

答案 1 :(得分:1)

你问,

  

方法引用只不过是匿名类的语法糖吗?

JLS说

  

评估方法引用表达式会生成功能接口类型的实例

JLS 8, section 15.13

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

实现可以识别对同一方法的多个引用并为它们使用相同的匿名类是合理的,但这种行为绝不是必需的,并且您已经证明您的实现不会这样做。但是,即使实现确实这样做,JLS至少暗示,方法引用表达式的每个评估都会产生一个新对象。

你继续,

  

如果没有,我需要做什么才能始终获得相同的方法参考? (除了在字段中存储一次引用以便使用。)

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

首先,为方法引用生成的对象的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都会获得自己生成的类。

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