Java 方法引用是否稳定?

2022-09-01 21:09:30

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

anObject::aMethod

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

例如,如果计划将它们用作可以添加和删除的回调,这很好:Runnable

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

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


答案 1

JLS 不承诺从方法引用表达式中获得的内容的标识或相等性。

您可以运行快速测试:

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 并使用 serlialized 表示形式键入回调映射。请参阅如何序列化 lambda?。虽然这可以工作,但并不完全需要按照规范工作。Serializable


答案 2

试试这个就可以得到答案:

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

答案是...很遗憾,没有。

当然,如果您保留相同的引用(即相同的对象),它将起作用;但是,如上例所示,如果您请求两个 lambda,尽管它们看起来相同,但它们永远不会相等。因此,然后以后显然会起作用,但是如果按字面意思这样写,那么从集合中将永远不会起作用。reference = object::methodremove(reference)remove(sameObject::sameMethod)

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

正如@Hitobat所指出的那样,如果你考虑一下lambdas到底是什么以及它们来自哪里,这种不相等是有道理的。基本上,是 的句法 suggar。如果没有适当的 Object.equals 重载,匿名类的两个实例显然是不同的。正如许多人可能一样,我虽然在某个地方有一种常用的lambda的缓存,以使其更有效率;嗯,一点也不。Supplier<String> x = myObject::toStringSupplier<String> x = new Supplier<String>( ... )


推荐