Composing Predicates – Functional-Style Programming

Composing Predicates

The predicate interfaces define default methods to compose compound predicates—that is, to chain together predicates with logical AND and OR operations.

Click here to view code image

default Predicate<T> negate()

Returns a predicate that represents the logical negation of this predicate.

Click here to view code image

default Predicate<T> and(Predicate<? super T> other)

Returns a composed predicate that represents a short-circuiting logical AND of this predicate and the predicate specified by the other parameter.

The other predicate is only evaluated if this predicate is true. Any exceptions thrown during the evaluation of either predicate are conveyed to the caller.

Click here to view code image

default Predicate<T> or(Predicate<? super T> other)

Returns a composed predicate that represents a short-circuiting logical OR of this predicate and the predicate specified by the other parameter.

The other predicate is only evaluated if this predicate is false. Any exceptions thrown during the evaluation of either predicate are conveyed to the caller.

Click here to view code image

static <T> Predicate<T> not(Predicate<? super T> target)

This static method returns a predicate that is the negation of the specified predicate.

Click here to view code image

static <T> Predicate<T> isEqual(Object targetRef)

This static method returns a predicate that tests if the argument in the call to the test() method and the targetRef object are equal—for example, Predicate.isEqual(“Aha”).test(“aha”). In contrast to the Object.equals() method, this method returns true if both the argument and the targetRef object are null.

At (1) below, the isPalindrome predicate is negated to define a predicate that tests if a string is not a palindrome.

The method calls in a compound predicate are executed from left to right with short-circuit evaluation of the predicates. The compound predicate x.or(y).and(z) at (2) is evaluated as ((x.or(y)).and(z)), where x, y, and z are constituent predicates isEvenLen, startsWithA, and isNotPalindrome, respectively. The or() method is executed first, followed by the and() method. However, the startsWithA predicate is only evaluated if the isEvenLen predicate was false—that is, the or() method tests the isEvenLen predicate first, but not the startsWithA predicate unless the first one is false. Analogously, the isNotPalindrome predicate is only evaluated if the predicate on which the and() method is invoked is true. Note that the same argument is passed to all the constituent predicates that comprise a compound predicate. Schematically, the evaluation of the method call composedPredicate.test(“Adda”) at (3) proceeds as follows:

Click here to view code image


((x.or(y)).and(z))
= ((true.or(y)).and(z))
= (true.and(z))
= (true.and(false))
= false
// A string that is not a palindrome.
Predicate<String> isNotPalindrome = isPalindrome.negate();       // (1)
// A string with even length or starts with an ‘A’, and is not a palindrome.
Predicate<String> composedPredicate
    = isEvenLen.or(startsWithA).and(isNotPalindrome);            // (2)
System.out.println(“Using composed predicate on \”Adda\”: “
        + composedPredicate.test(“Adda”));                       // (3) false.
// A string with even length, or it starts with an ‘A’ and is not a palindrome.
Predicate<String> conditionalOperators
    = str -> str.length() % 2 == 0 || str.startsWith(“A”)        // (4)
          && !(new StringBuilder(str).reverse().toString().equalsIgnoreCase(str));
System.out.println(“Using conditional operators on \”Adda\”: “
        + conditionalOperators.test(“Adda”));                    // (5) true.

The evaluation should be contrasted with the predicate at (4) that is defined with the negation operator ! and the short-circuit conditional operators || and &&. Note that the evaluation order is different for the conditional operators because of the precedence rules: a || b && !c is evaluated as (a || (b && (!c))), where a, b, and care boolean expressions str.length() % 2 == 0, str.startsWith(“A”), and (new String-Builder(str).reverse().toString().equalsIgnoreCase(str)), respectively. Schematically, the evaluation of the method call conditionalOperators.test(“Adda”) at (5) proceeds as follows:
(a || (b && (!c)))
= (a || (b && (!true)))
= (a || (b && false))
= (a || (false))
= (true || false)
= true

Using equality predicates is illustrated by the following examples:

Click here to view code image

Predicate<String> isEqualToTarget = Predicate.isEqual(“Ada”);
System.out.println(isEqualToTarget.test(“Adda”));           // false.
System.out.println(Predicate.isEqual(“Ada”).test(“Ada”));   // true.
System.out.println(Predicate.isEqual(“null”).test(“null”)); // true.

Leave a Comment