Predicate Functional Interface in Java

Predicate interface in Java is another Functional interface introduced in version 8 and is part of the java.util.function package.

It has one functional method test(Object obj) that takes one input parameter, performs some operations, and returns a boolean.

Since it is a Functional Interface, we can implement it with a Lambda expression.

Predicate<T>

@FunctionalInterface
public interface Predicate<T> {
    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * Returns a predicate that represents the logical negation of this
     * predicate.
     *
     * @return a predicate that represents the logical negation of this
     * predicate
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * OR of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code true}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * 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);
    }

    /**
     * Returns a predicate that is the negation of the supplied predicate.
     * This is accomplished by returning result of the calling
     * {@code target.negate()}.
     *
     * @param <T>     the type of arguments to the specified predicate
     * @param target  predicate to negate
     *
     * @return a predicate that negates the results of the supplied
     *         predicate
     *
     * @throws NullPointerException if target is null
     *
     * @since 11
     */
    @SuppressWarnings("unchecked")
    static <T> Predicate<T> not(Predicate<? super T> target) {
        Objects.requireNonNull(target);
        return (Predicate<T>)target.negate();
    }
}


As you can see, in addition to the single abstract method, there are several other default and static methods.

T in Predicate<T> means it can accept an object of any type.

Implementing the Predicate interface

Example 1:

public class Test {

  public static void main(String[] args) {
    Predicate<Integer> p1 = number -> number % 2 == 0;

    System.out.println(p1.test(4));
  }
}
Output: true
 
Here, inside the braces <>, we specified the input type, and we used a Lambda expression to write the implementation of the Predicate interface. 
 
On the left side of the arrow sign (->), we have the input, and on the right side, we wrote a logic that tests if the input is an even number.
 
Note: If we have a single statement inside the Lambda body, then we don’t need to put the curly braces {}.
The same for the input parameter, if we are dealing with only one, we don’t need to put it between the parentheses ()
 
Also, if we have a single statement that returns some result, we don’t have to put the return keyword, it would be redundant.
 
The advantage of using the Predicate interface is that we can reuse it anywhere in the code. We can pass it as a method parameter also.
 
Now let’s implement other methods.
 
Note: All other methods except isEqual() need to be followed by a call to the test() method to pass the parameter.

Example 2:

Implementation of the and(Predicate<? super T> other) method that accepts the Predicate and returns the Predicate also. 

class Test {

  public static void main(String[] args) {
    Predicate<Integer> lessThan = number -> number < 20;
    Predicate<Integer> greaterThan = number -> number > 30;

    System.out.println(lessThan.and(greaterThan).test(12));
  }
}
Output: false
 
Here we used the and() method to chain the predicates. The and() method acts as the short-circuiting logical AND operator. It will return true only if both predicates return true.
 
In the example above, we got false because the greaterThan predicate returns false.

Example 3:

Implementation of the or(Predicate<? super T> other) method that accepts the Predicate and returns the Predicate also.

Like the and(Predicate<? super T> other) method. The difference is that or() method returns true if any of the predicates return true.

class Test {

  public static void main(String[] args) {
    Predicate<Integer> lessThan = number -> number < 20;
    Predicate<Integer> greaterThan = number -> number > 30;

    System.out.println(lessThan.or(greaterThan).test(12));
  }
}
Output: true

Example 4:

Implementation of the Predicate<T> negate() method that does not accept any inputs and returns the Predicate interface. 

class Test {

  public static void main(String[] args) {
    Predicate<Integer> isEvenNumber = number -> number % 2 == 0;

    System.out.println(isEvenNumber.negate().test(12));
  }
}
Output: false
 
As you can see, the negate() method will negate the result returned from the Predicate. It will convert true to false and vice versa.

Example 5:

Implementation of the static Predicate<T> isEqual(Object targetRef) method that tests if two arguments are equal according to Objects.equals(Object, Object).

class Test {

  public static void main(String[] args) {
    Predicate<String> p1 = Predicate.isEqual("Java 8 features");

    System.out.println(p1.test("Java 8 features"));
  }
}
Output: true
 
Here, we tested if the input parameter of the test() method is equal to the String parameter that we passed to the isEqual() method above. And since it is equal, we got true as a result.
 
That was all regarding the Predicate interface in Java. Proceed to the next lesson to learn more about BiPredicate Functional Interface.
 
Happy coding!

Leave a Reply

Your email address will not be published. Required fields are marked *