Java 有没有办法比较 lambdas?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24095875/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a way to compare lambdas?
提问by Peter Lawrey
Say I have a List of object which were defined using lambda expressions (closures). Is there a way to inspect them so they can be compared?
假设我有一个使用 lambda 表达式(闭包)定义的对象列表。有没有办法检查它们以便进行比较?
The code I am most interested in is
我最感兴趣的代码是
List<Strategy> strategies = getStrategies();
Strategy a = (Strategy) this::a;
if (strategies.contains(a)) { // ...
The full code is
完整的代码是
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 e.g. 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";
}
}
It would appear the only solution is to define each lambda as a field and only use those fields. If you want to print out the method called, you are better off using Method
. Is there a better way with lambda expressions?
似乎唯一的解决方案是将每个 lambda 定义为一个字段并仅使用这些字段。如果要打印出调用的方法,最好使用Method
. lambda 表达式有更好的方法吗?
Also, is it possible to print a lambda and get something human readable? If you print this::a
instead of
另外,是否可以打印 lambda 并获得人类可读的内容?如果您打印this::a
而不是
ClosureEqualsMain$$Lambda/821270929@3f99bd52
get something like
得到类似的东西
ClosureEqualsMain.a()
or even use this.toString
and the method.
甚至使用this.toString
和方法。
my-ClosureEqualsMain.a();
采纳答案by Brian Goetz
This question could be interpreted relative to the specification or the implementation. Obviously, implementations could change, but you might be willing to rewrite your code when that happens, so I'll answer at both.
这个问题可以相对于规范或实现来解释。显然,实现可能会发生变化,但您可能愿意在发生这种情况时重写您的代码,所以我会同时回答。
It also depends on what you want to do. Are you looking to optimize, or are you looking for ironclad guarantees that two instances are (or are not) the same function? (If the latter, you're going to find yourself at odds with computational physics, in that even problems as simple as asking whether two functions compute the same thing are undecidable.)
这也取决于你想做什么。您是在寻求优化,还是在寻找可靠的保证,即两个实例是(或不是)相同的功能?(如果是后者,你会发现自己与计算物理学不一致,因为即使是问两个函数是否计算同一件事这样简单的问题也是不可判定的。)
From a specification perspective, the language spec promises only that the result of evaluating (not invoking) a lambda expression is an instance of a class implementing the target functional interface. It makes no promises about the identity, or degree of aliasing, of the result. This is by design, to give implementations maximal flexibility to offer better performance (this is how lambdas can be faster than inner classes; we're not tied to the "must create unique instance" constraint that inner classes are.)
从规范的角度来看,语言规范仅承诺评估(而不是调用) lambda 表达式的结果是实现目标功能接口的类的实例。它不保证结果的身份或混叠程度。这是设计使然,为实现提供最大的灵活性以提供更好的性能(这就是 lambda 比内部类更快的原因;我们不受内部类的“必须创建唯一实例”约束。)
So basically, the spec doesn't give you much, except obviously that two lambdas that are reference-equal (==) are going to compute the same function.
所以基本上,规范没有给你太多,除了很明显两个引用相等 (==) 的 lambda 将计算相同的函数。
From an implementation perspective, you can conclude a little more. There is (currently, may change) a 1:1 relationship between the synthetic classes that implement lambdas, and the capture sites in the program. So two separate bits of code that capture "x -> x + 1" may well be mapped to different classes. But if you evaluate the same lambda at the same capture site, and that lambda is non-capturing, you get the same instance, which can be compared with reference equality.
从实现的角度来看,您可以得出更多结论。实现 lambda 表达式的合成类与程序中的捕获站点之间存在(目前,可能会改变)1:1 的关系。因此,捕获“x -> x + 1”的两个单独的代码位很可能映射到不同的类。但是,如果您在同一个捕获站点评估相同的 lambda,并且该 lambda 是非捕获的,您将获得相同的实例,可以将其与引用相等进行比较。
If your lambdas are serializable, they'll give up their state more easily, in exchange for sacrificing some performance and security (no free lunch.)
如果你的 lambda 是可序列化的,它们会更容易放弃它们的状态,以换取牺牲一些性能和安全性(没有免费的午餐。)
One area where it might be practical to tweak the definition of equality is with method references, because this would enable them to be used as listeners and be properly unregistered. This is under consideration.
调整相等性定义可能可行的一个领域是方法引用,因为这将使它们能够用作侦听器并正确取消注册。这是在考虑中。
I think what you're trying to get to is: if two lambdas are converted to the same functional interface, are represented by the same behavior function, and have identical captured args, they're the same
我想你想要的是:如果两个 lambdas 被转换为相同的功能接口,由相同的行为函数表示,并且具有相同的捕获参数,它们是相同的
Unfortunately this is both hard to do (for non-serializable lambdas, you can't get at all the components of that) and not enough (because two separately compiled files could convert the same lambda to the same functional interface type, and you wouldn't be able to tell.)
不幸的是,这很难做到(对于不可序列化的 lambda,您无法获得其中的所有组件)而且还不够(因为两个单独编译的文件可以将相同的 lambda 转换为相同的功能接口类型,而您不会不能说。)
The EG discussed whether to expose enough information to be able to make these judgments, as well as discussing whether lambdas should implement more selective equals/hashCode or more descriptive toString. The conclusion was that we were not willing to pay anything in performance cost to make this information available to the caller (bad tradeoff, punishing 99.99% of users for something that benefits .01%).
EG 讨论了是否要公开足够的信息来做出这些判断,以及讨论 lambda 是否应该实现更具选择性的 equals/hashCode 或更具描述性的 toString。结论是我们不愿意为向调用者提供这些信息而付出任何性能成本(糟糕的权衡,惩罚 99.99% 的用户,因为它有 0.01% 的好处)。
A definitive conclusion on toString was not reached, but left open to be revisited in the future. However, there were some good arguments made on both sides on this issue; this is not a slam-dunk.
尚未就 toString 得出明确结论,但仍有待将来重新审视。然而,双方在这个问题上都有一些很好的论据;这不是灌篮。
回答by F. B?ller
I don't see a possibility, to get those informations from the closure itself. The closures doesn't provide state.
我看不到从闭包本身获取这些信息的可能性。闭包不提供状态。
But you can use Java-Reflection, if you want to inspect and compare the methods. Of course that is not a very beautiful solution, because of the performance and the exceptions, which are to catch. But this way you get those meta-informations.
但是如果你想检查和比较这些方法,你可以使用 Java-Reflection。当然,这不是一个非常漂亮的解决方案,因为性能和要捕获的异常。但是通过这种方式,您可以获得这些元信息。
回答by KIC
To compare labmdas I usually let the interface extend Serializable
and then compare the serialized bytes. Not very nice but works for the most cases.
为了比较 labmdas,我通常让接口扩展Serializable
,然后比较序列化的字节。不是很好,但适用于大多数情况。