1

With the functional interfaces introduced in Java 8, you can easily chain different expressions into one new expression, illustrated by the code snippet below.

public class PredicateChaining {
    public static void main(String[] args) {
        // verbose, but standard JDK functionality only
        Predicate<String> allUpperCase = StringUtils::isAllUpperCase;
        Predicate<String> longerThan5 = s -> s.length() > 5;
        if (allUpperCase.and(longerThan5).test("PREDICATE")) {
            System.out.println("'PREDICATE' is a uppercase text, longer than 5.");
        }

        // compact, but with utility method ('chain' in this case)
        if (chain(StringUtils::isAllLowerCase).and(s -> s.length() < 5).test("test")) {
            System.out.println("'test' is a lowercase text, shorter than 5.");
        }
    }

    public static <T> Predicate<T> chain(Predicate<T> test) {
        return test;
    }
}

In this case 2 String Predicates are chained into one new Predicate. This works fine when you have at least one handle (i.e. variable) to invoke the chaining method (in this case 'and') on. However, if the expressions you want to chain are represented as a lambda or method reference, you need to translate one of them explicitly to a variable, which makes it very verbose. When the compiler is unable to determine the exact type, you have to resort to this kind of construction. But in most cases, this is simply verbose overhead I'd like to avoid.

In this example, I have written the utility method 'chain' myself. But I can create one for the other functional interfaces (Function, Consumer, Supplier, BiConsumer, ...) as well.

Instead of creating my own utility class with these methods (which I would probably copy-paste to every project I work on), I want to know whether such a class exists in the JDK or in some library?

On the other hand, if such a class does not exist and I shouldn't be creating one myself, I would like to hear why.

Erwin Dupont
  • 547
  • 4
  • 11
  • 1
    A [similar question has already been answered](http://stackoverflow.com/a/24396579/1571325), stating essentially the same @holger did. – Erwin Dupont Feb 19 '15 at 12:45

1 Answers1

5

There is no sense in creating Predicate instances to use them in a follow-up if statement.

Predicate instances are normally used for passing a condition to a method. In this case, you may study the particular API to find out whether such a chaining is possible using natural constructs.

E.g. when passing Predicates to a stream you may simply make two or more filter calls:

stringStream.filter(StringUtils::isAllUpperCase).filter(s->s.length()>5). …

(and you may similarly chain Functions using multiple map calls)

and other APIs may have similar methods.

But after all, if you have two conditions which you want to combine and neither is already a Predicate instance, the straight forward solution is to just combine the expressions:

stringStream.filter(s -> isAllUpperCase(s) && s.length()>5). …

or as stand-alone creation:

Predicate<String> allUpperCaseAndLongerThan5 = s -> isAllUpperCase(s) && s.length()>5;

note that you can use a static import for the utility method here which allows an even more compact expression.

Using Predicate.and and Predicate.or is only useful if you already have a Predicate instance and want to augment its logic. But in this case you also have an instance on which you can invoke and or or without problems.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 1
    Btw, you don’t need any utility method at all: `StringUtils.isAllUpperCase(s)` can be replaced with `s.chars().allMatch(Character::isUpperCase)`. – Holger Feb 19 '15 at 12:11