Java方法引用是否稳定?

时间:2018-06-07 19:04:54

标签: java java-8

如果我使用新语法获取方法引用:

anObject::aMethod

我总是得到同一个对象吗?也就是说,我可以相信对同一方法的两个引用是相同的吗?

很高兴知道,例如,我是否打算将它们用作我可以添加和删除的Runnable回调:

someLibrary.addCallback(anObject::aMethod)
// later
someLibrary.removeCallback(sameObject::sameMethod)

这是否需要在Runnable变量中保存引用以保持其稳定?

2 个答案:

答案 0 :(得分:17)

JLS makes no promises关于从方法引用表达式中获取的内容的身份或相等性。

您可以进行快速测试:

Object obj = new Object();

IntSupplier foo = obj::hashCode;
IntSupplier bar = obj::hashCode;

System.out.println(foo == bar);  // false

System.out.println(foo.equals(bar));  // false      

但这当然是依赖于实现的。

可以制作你的lambda Serializable并使用serlialized表示键回你的回调映射。见How to serialize a lambda?。虽然这样可行,但并不完全需要按规格工作。

答案 1 :(得分:7)

试试这个以获得答案:

Object object = ...;
Supplier<String> s1 = object::toString;
Supplier<String> s2 = object::toString;
System.out.println(s1.equals(s2));

答案是......不幸的是没有。

当然,如果你保持相同的参考(即同一个对象),它将起作用;但是,如果像上面的例子那样,你要求两个lambdas,虽然它们似乎是相同的,但它们永远不会相等。 因此,reference = object::method以及之后的remove(reference)显然会有效,但如果它是文字书面的,那么集合中的remove(sameObject::sameMethod)将永远不会有效。

对于构造函数(例如ArrayList :: new)和未绑定方法(例如Object :: toString),答案也是否定的。似乎每次使用lambda表达式时都会构造一个新的lambda。

正如@Hitobat指出的那样,如果你想一下lambda究竟是什么以及它们来自哪里,这种不平等是有道理的。基本上,Supplier<String> x = myObject::toStringSupplier<String> x = new Supplier<String>( ... )的句法糖。如果没有正确的Object.equals重载,匿名类的两个实例显然是不同的。可能和很多人一样,我觉得在某个地方有一种经常使用的lambdas缓存可以提高效率;好吧,根本不是。