Lately I've been fiddling around with the new java8 features to have a better understanding of them.
When trying some stuff with Stream.filter
I came across the source of Predicate.java
in which I found the following implementation of the isEqual
method:
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param <T> the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
What made me wonder was this line: : object -> targetRef.equals(object);
.
Maybe I am massively overthinking this, but I couldn't help immediately thinking why that line wasn't : targetRef::equals;
like this:
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: targetRef::equals;
}
Seems to me a unnecessary creation of a lambda otherwise.
Unless I'm missing something, to me those do the same. (They are the same right?) Is there any reason why the current implementation was chosen? Or is this just something incredibly small that it just got overlooked or nobody really cared.
Guess that actually results in a bonus question: Is there any benefit in using one way over the other? Like some kind of (probably very small) performance bonus?
The predicate is a predefined functional interface in Java defined in the java. util. Function package. It helps with manageability of code, aids in unit-testing, and provides various handy functions.
Java IntPredicate interface is a predicate of one int-valued argument. It can be considered an operator or function that returns a value either true or false based on certain evaluation on the argument int value. IntPredicate is a functional interface whose functional method is boolean test(int a) .
Why was it done this way?
Pure speculation but, perhaps when it was written the developer was more comfortable with the -> and possibly :: didn't even work at that time. The libraries were being written while they were fixing bugs in the compiler.
There is no way to know and even the person who write it probably doesn't remember.
Whether you use a method reference or a closure syntax, the same number of objects is created in most cases (as in this example)
Guess that actually results in a bonus question: Is there any benefit in using one way over the other? Like some kind of (probably very small) performance bonus?
Using a method reference means one less method call. This could have an indirect impact on inlining as the number of levels is limited to 9 by default. For example
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
Consumer<String> lambda = s-> printStackTrace(s);
lambda.accept("Defined as a lambda");
Consumer<String> methodRef = Main::printStackTrace;
methodRef.accept("Defined as a method reference");
}
static void printStackTrace(String description) {
new Throwable(description).printStackTrace();
}
}
prints
java.lang.Throwable: Defined as a lambda
at Main.printStackTrace(Main.java:15)
at Main.lambda$main$0(Main.java:6)
at Main.main(Main.java:7)
java.lang.Throwable: Defined as a method reference
at Main.printStackTrace(Main.java:15)
at Main.main(Main.java:10)
In the first case, the compiler has generated a method called Main.lambda$main$0
which contains the code which actually calls printStackTrace
Where using one or the other makes more difference is when you could have a capturing (saves a value) or non-capturing lambda. A non-capturing lambda is only created once.
e.g.
Consumer<String> print1 = System.out::println; // creates an object each time
Consumer<String> print2 = s->System.out.println(s); // creates an object once.
In the first case, if you call System.setOut
it will ignore this change as it has it's own copy of the PrintStream to write to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With