有没有办法比较lambdas?

假设我有一个使用lambdaexpression式(闭包)定义的对象列表。 有没有办法检查他们,以便他们可以比较?

我最感兴趣的代码是

List<Strategy> strategies = getStrategies(); Strategy a = (Strategy) this::a; if (strategies.contains(a)) { // ... 

完整的代码是

 import java.util.Arrays; import java.util.List; public class ClosureEqualsMain { interface Strategy { void invoke(/*args*/); default boolean equals(Object o) { // doesn't compile return Closures.equals(this, o); } } public void a() { } public void b() { } public void c() { } public List<Strategy> getStrategies() { return Arrays.asList(this::a, this::b, this::c); } private void testStrategies() { List<Strategy> strategies = getStrategies(); System.out.println(strategies); Strategy a = (Strategy) this::a; // prints false System.out.println("strategies.contains(this::a) is " + strategies.contains(a)); } public static void main(String... ignored) { new ClosureEqualsMain().testStrategies(); } enum Closures {; public static <Closure> boolean equals(Closure c1, Closure c2) { // This doesn't compare the contents // like others immutables eg String return c1.equals(c2); } public static <Closure> int hashCode(Closure c) { return // a hashCode which can detect duplicates for a Set<Strategy> } public static <Closure> String asString(Closure c) { return // something better than Object.toString(); } } public String toString() { return "my-ClosureEqualsMain"; } } 

这似乎是唯一的解决scheme是将每个lambda定义为一个字段,只使用这些字段。 如果要打印出所调用的方法,最好使用Method 。 lambdaexpression式有更好的方法吗?

另外,是否可以打印一个lambda并获得一些人类可读的东西? 如果你打印this::a而不是

 ClosureEqualsMain$$Lambda$1/821270929@3f99bd52 

得到类似的东西

 ClosureEqualsMain.a() 

甚至使用this.toString和方法。

 my-ClosureEqualsMain.a(); 

这个问题可以解释为相对于规范或实现。 显然,实现可能会改变,但是当这种情况发生时,你可能会重写你的代码,所以我会在两者中都回答。

这也取决于你想要做什么。 你正在寻找优化,还是你正在寻找ironclad保证两个实例是(或不是)相同的function? (如果是后者,你会发现自己与计算物理学不一致,即使是问两个函数是否计算相同的事情也是如此简单的问题是不可判定的。)

从规范的angular度来看,语言规范只承诺评估(不调用)lambdaexpression式的结果是实现目标函数接口的类的一个实例。 它对结果的身份或别名程度没有任何承诺。 这是为了给予实现最大的灵活性,以提供更好的性能(这是lambda可以比内部类更快;我们不绑定到“必须创build唯一的实例”约束,内部类)。

所以基本上,规范并没有给你太多,除非明显的是两个参考相等的lambda(==)将计算相同的函数。

从实施的angular度来看,你可以再多一点。 在实现lambdas的合成类和程序中的捕获站点之间存在(当前可能会改变)1:1关系。 因此,捕获“x – > x + 1”的两个单独的代码位可能被映射到不同的类。 但是,如果您在相同的捕获站点上评估相同的lambda,并且lambda不捕获,则会得到相同的实例,可以将其与参考相等进行比较。

如果你的lambdas是可序列化的,他们将更容易放弃他们的状态,换取牺牲一些性能和安全性(没有免费的午餐)。

调整平等定义的一个方面可能是实用的方法引用,因为这将使它们能够被用作监听器并被正确地注销。 这正在考虑之中。

我想你想要得到的是:如果两个lambda被转换为相同的函数接口,由相同的行为函数表示,并具有相同的捕获参数,它们是相同的

不幸的是,这是很难做到的(对于不可序列化的lambdaexpression式,你不能得到所有的组件),并不够(因为两个单独编译的文件可以将相同的lambda转换为相同的函数接口types,无法分辨。)

EG讨论了是否公开足够的信息来做出这些判断,以及讨论lambda是否应该实现更多的select性equals / hashCode或者更多描述性的toString。 结论是,我们不愿意付出任何性能成本来向调用者提供这些信息(坏的折衷,惩罚99.99%的用户获得0.01%的好处)。

关于toString的确切结论还没有达成,但未来还是有待重新审视的。 不过,双方在这个问题上有一些很好的论点, 这不是一个扣篮。

我不认为有可能从封闭本身获取这些信息。 closures不提供状态。

但是如果你想检查和比较这些方法,你可以使用Java-Reflection。 当然,这不是一个非常漂亮的解决scheme,因为性能和例外,这是要抓住的。 但是这样你就可以获得这些元信息。

为了比较labmdas我通常让接口扩展Serializable ,然后比较序列化的字节。 不是很好,但为大多数情况下工作。