So assuming I use some random filter on a stream, the most straightforward way is to directly input the Predicate:
x.stream().filter(e -> e % 2 == 0)
As well I can simply make a reference and define the Predicate in advance:
Predicate<Integer> isEven = e -> e % 2 == 0;
...
x.stream().filter(isEven)
But I can as well use a function:
private static boolean isEven(Integer integer) {
return integer % 2 == 0;
}
...
x.stream().filter(MyClass::isEven)
As far as I can tell, the Predicate is of course much more limited while the function might have side effects etc. But since people like Venkat Subramaniam use the latter solution, I really wonder: What are the main differences here?
No! Predicate is not really limited in comparison to a method reference! In fact, these things are all the same!
Just look at the filter()
function signature:
filter(Predicate<? super T> predicate)
And let's consider your examples:
x.stream().filter(e -> e % 2 == 0)
Predicate<Integer> isEven = e -> e % 2 == 0;
...
x.stream().filter(isEven)
The first one is just an inlined version of the latter.
private static boolean isEven(Integer integer) {
return integer % 2 == 0;
}
...
x.stream().filter(MyClass::isEven)
and here you can see Method References
in action. MR are just a syntactic sugar allowing you to define Lambda Expression based on already existing functions.
At the end of the day all those expressions become the same implementation of Predicate functional interface.
Also, you can also perform side effects in your Lambda Expressions by using block syntax on the right side but it's generally not advised:
e -> {
//side effects here
return e % 2 == 0;
}