17

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?

AdHominem
  • 1,204
  • 3
  • 13
  • 32

3 Answers3

13

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;
}
Grzegorz Piwowarek
  • 13,172
  • 8
  • 62
  • 93
  • I see, without MR we would only be able to define Predicates which are really limited concerning their reusability. So this leads to the question what is the cleaner way of writing? Using a MR, a Predicate or the actual statement itself? – AdHominem Apr 22 '16 at 16:54
  • 1
    @AdHominem it depends on your context. If you are not really going to reuse your predicate, then just stick to a simple lambda expression, but if you are going to keep reusing it I suggest creating a static Predicate instance in some utils class without creating a method itself – Grzegorz Piwowarek Apr 22 '16 at 16:57
6

When looking at it from the point of view of building a library of reusable Predicates, a library of functions that return a boolean is far more versatile collection of predicates than a library of static final Predicate instances. Why?

Consider a library that contains the following:

public static boolean isEven(int i) { return i -> i % 2 == 0; }

vs.

public static final Predicate<Integer> IS_EVEN = i -> i % 2 == 0; 
  • if library functions are named well, they read better. When revisiting code after a long absence it's easier to scan filter(MyLib::isEven) than filter(i -> i % 2 == 0) and filter(MyLib::isEven) tells you exactly what is being invoked, while filter(MyLib.IS_EVEN) does not
  • If you just want to call your library function instead of use it as a predicate, it's more readable. MyLib.isEven(i) scans better than MyLib.IS_EVEN.test(i)
  • If you need to use an IntPredicate instead of Predicate<Integer> a Guava Predicate<Integer>, Apache Collections4 Predicate<Integer>, etc., with a library function you just continue to do MyLib::isEven. With a static final Predicate<Integer> instance you would have to convert it by doing MyLib.IS_EVEN::test (and you end up using a method reference anyway)

The same reasoning applies to all functional types. Write functions. They can be applied to any functional type that matches the signature with a simple method reference.

Hank D
  • 6,271
  • 2
  • 26
  • 35
0

Both will return a filtered stream. Using predicate(function that return true or false) is more readable.... Here is a short hand for the above using Function.

  public class Demo {

    public static Function<Integer, Predicate<Integer>> isGreaterThanNumberIamAboutToApply = pivot -> number -> number % pivot == 0;

    public static void main(String[] args) {


        List<Integer> myInts = Arrays.asList(new Integer[]{1, 2, 3, 5, 6, 7, 7})
                .stream()
                .filter(isGreaterThanNumberIamAboutToApply
                        .apply(3)).collect(Collectors.toList());

        myInts.forEach(x -> System.out.printf("%s\n", x));

    }

}
z atef
  • 7,138
  • 3
  • 55
  • 50