Is there a way to compare lambdas?
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.)
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.
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.
If your lambdas are serializable, they'll give up their state more easily, in exchange for sacrificing some performance and security (no free lunch.)
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
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.)
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%).
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.
Java 8 Lambda: Comparator
Comparator#compareTo
returns an int
; while getTime
is obviously long
.
It would be nicer written like this:
.sort(Comparator.comparingLong(Message::getTime))
How does a lambda function become the compare() method of Comparator
Explanation
Comparator
is a functional interface (only demands one method). Thus, you can create instances of it by using a lambda expression.
It behaves very similar to other methods of creating instances, such as a regular class which extends or an anonymous class.
The lambda refers to the one method a functional interface demands. Since there is only one method, it is not ambiguous. The lambda names the input arguments and then gives an implementation for the method (it provides a body).
Overview
You have the following options to create instances of interfaces or abstract classes:
- Create a class that extends and use new
- Use an anonymous class
Supposed we have an interface which only offers one method (it's called functional interface then), we additionally have the following two options to create instances of it:
- Use a lambda expression
- Use a method reference
As example, we want to create a multiplication instance, using the following interface:
@FunctionalInterface
public interface Operation {
int op(int a, int b);
}
Create a class that extends and use new:
public class Multiplicator implements Operation {
@Override
public int op(int a, int b) {
return a * b;
}
}
// Usage
Operation operation = new Multiplicator();
System.out.println(operation.op(5, 2)); // 10Use an anonymous class:
Operation operation = new Operation() {
@Override
public int op(int a, int b) {
return a * b;
}
};
// Usage
System.out.println(operation.op(5, 2)); // 10Use a lambda expression:
Operation operation = (a, b) -> a * b;
System.out.println(operation.op(5, 2)); // 10Use a method reference:
// Somewhere else in our project, in the `MathUtil` class
public static int multiply(int a, int b) {
return a * b;
}
// Usage
Operation operation = MathUtil::multiply;
System.out.println(operation.op(5, 2)); // 10
How to compare functional interfaces / method references in java 8
The target type for a method reference, or a lambda, should be a functional interface. Since equals()
method takes an Object
, which is not a functional interface, it fails to compile. Now you would say, why? Well, a lambda expression or a method reference is at runtime implemented as an instance of a class implementing that functional interface. A FI contains just a single abstract method, thus for a lambda x -> Sysout(x)
, the left part becomes the parameter of that method, and right part becomes body.
Now there can be many functional interface, providing such method signature. That means same lambda expression can be compiled to implementation of different FIs. Now when you pass a lambda to an Object
reference like this:
Object ob = x -> Sysout(x);
which FI do you expect JVM to instantiate? This results in certain ambiguity, and hence is not allowed. But by pre-assigning a lambda to a FI reference:
Consumer<Object> consumer = x -> Sysout(x);
you've assigned a concrete meaning to the lambda, which then later on can be assigned to any of its super type reference.
Object ob = consumer;
Now as for why equals()
method returns false
, you can guess it. Since lambda is an instance of a class constructed at runtime, which would provide implementation of the abstract method in FI, on what basis would you want two Consumer
references to compare? Since there is no equals()
method overridden, it will invoke the implementation in Object
class, which just compares the reference. Implementation looks like this:
public boolean equals(Object ob) {
return this == ob;
}
Certainly aConsumer == bConsumer
will return false
, if both are referring to 2 different lambdas / method references.
Related Topics
How to Count the Number of Matches for a Regex
How to Execute Cucumber Feature File Parallel
How to Wait for a Number of Threads to Complete
Wanting a Type of Grid for a Pixel Editor
How to Exclude Some Concrete Urls from <Url-Pattern> Inside <Filter-Mapping>
Mousemotionlistener in Java Swing, Using It with Components Inside Components etc
How to Interrupt a Serversocket Accept() Method
How to Convert an Array to a Set in Java
Differencebetween Field, Variable, Attribute, and Property in Java Pojos
Java Function for Arrays Like PHP's Join()
Difference Between 'Optional.Orelse()' and 'Optional.Orelseget()'
Java Map with Values Limited by Key's Type Parameter