Is there a way to compare lambda?

Let's say I have a list of objects that were defined using lambda expressions (closures). Is there any way to test them so that they can be compared?

The code that interests me the most is

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

Full code

 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"; } } 

It seems the only solution is to define each lambda as a field and use only these fields. If you want to print the called method, you are better off using Method . Is there a better way with lambda expressions?

Also, is it possible to print a lambda and get something human readable? If you print this::a instead

 ClosureEqualsMain$$Lambda$1/821270929@3f99bd52 

get something like

 ClosureEqualsMain.a() 

or even use this.toString and the method.

 my-ClosureEqualsMain.a(); 
+61
java lambda java-8
Jun 07 '14 at 9:40
source share
3 answers

This question may be construed with respect to specification or implementation. Obviously, implementations may change, but you may want to rewrite your code when this happens, so I will answer both.

It also depends on what you want to do. Do you want to optimize or are you looking for reliable guarantees that two instances (or are not) of the same function? (If the latter, you will encounter computational physics, that even problems as simple as the question of whether two functions can calculate the same thing are unsolvable.)

From the point of view of the specification, the specification of the promises language consists only in the fact that the result of computing (not calling) the lambda expression is an instance of a class that implements the target functional interface. He does not make promises about the identity or degree of smoothing of the result. This is by design to provide maximum implementation flexibility, to provide better performance (since lambdas can be faster than inner classes, we are not tied to the "need to create a unique instance" constraint that contains inner classes.)

Basically, the specification does not give you much, except, obviously, the two lambdas that are referenced (==) are going to compute the same function.

In terms of implementation, you can do a few more. There is (currently subject to change) a 1: 1 ratio between synthetic classes that implement lambda and capture points in the program. Thus, two separate code bits that capture "x โ†’ x + 1" can be mapped to different classes. But if you evaluate the same lambda in the same capture area and that the lambda does not capture, you get the same instance that can be compared with reference equality.

If your lambdas are serializable, they can easily give up their fortune in exchange for sacrificing some performance and security (without a free lunch.)

One area where it would be useful to set up a definition of equality is to refer to methods, as this will allow them to be used as listeners and to be properly unregistered. This is being considered.

I think you are trying to understand: if two lambdas are converted to the same functional interface, they are represented by the same behavior function and have the same captured arguments, they are the same

Unfortunately, this is difficult to do (for non-serializable lambdas, you cannot get on all its components) and not enough (since two separately compiled files can convert the same lambdas to the same type of functional interface, and you cannot tell. )

EG discussed whether sufficient information should be provided to be able to make these judgments, and whether lambdas should use more selective equals / hashCode or more descriptive toString. The conclusion was that we did not want to pay anything at the cost of execution in order to make this information available to the caller (a bad compromise, punishing 99.99% of users for the benefit of 0.01%).

The final conclusion about toString was not reached, but remained open for revision in the future. However, good arguments were expressed in favor of both sides on this issue; this is not spanking.

+56
Jun 07 '14 at 15:37
source share

I see no way to get information from the closure itself. Closing does not provide state.

But you can use Java-Reflection if you want to test and compare methods. Of course, this is not a pretty solution because of performance and exceptions that need to be caught. But in this way you get these meta information.

+6
Jun 07 '14 at 9:54 on
source share

For comparing labmdas, I usually let the interface extend Serializable , and then compare serialized bytes. Not very nice, but it works in most cases.

+5
Apr 22 '16 at 12:50
source share



All Articles