如果我使用新语法获取方法引用:
anObject::aMethod
我总是得到同一个对象吗?也就是说,我可以相信对同一方法的两个引用是相同的吗?
很高兴知道,例如,我是否打算将它们用作我可以添加和删除的Runnable
回调:
someLibrary.addCallback(anObject::aMethod)
// later
someLibrary.removeCallback(sameObject::sameMethod)
这是否需要在Runnable
变量中保存引用以保持其稳定?
答案 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::toString
是Supplier<String> x = new Supplier<String>( ... )
的句法糖。如果没有正确的Object.equals重载,匿名类的两个实例显然是不同的。可能和很多人一样,我觉得在某个地方有一种经常使用的lambdas缓存可以提高效率;好吧,根本不是。