Google Guava Predicates

Introduction

In this post, let us learn about the useful static utility methods in the Google Guava Predicates class.

Google Guava Predicate vs Java Predicate

A Google Guava Predicate class extends the Java Predicate (java.util.function.Predicate). This is similar to the Java supplier vs Google Guava supplier case. Hence, we can use the Guava Predicate where a Java predicate is used. Refer to the linked section on the Suppliers post to know more.

Predicates – alwaysFalse and alwaysTrue

The alwaysTrue method returns a predicate that always evaluates to true whatever argument we pass. Similarly, the alwaysFalse returns a predicate which evaluates to false.

Predicate<String> truePredicate = Predicates.alwaysTrue();
System.out.println(truePredicate.test("any-value")); //true
System.out.println(truePredicate.test("")); //true

Predicate<String> falsePredicate = Predicates.alwaysFalse();
System.out.println(falsePredicate.test("any-value")); //false
System.out.println(falsePredicate.test("")); //false

Note that I’m using the Java predicate as the type on the LHS as a Google Guava predicate is a Java predicate as explained before.

instanceOf and subTypeOf

instanceOf

The Predicates#instanceOf takes a Class and returns a predicate which tests if the object being tested is an instance of the passed class. Calling instanceOf() will return a Predicate<Object>.

Predicate<Object> numberClassPredicateForInstanceOf = Predicates.instanceOf(Number.class);
Integer i = 1;
System.out.println(numberClassPredicateForInstanceOf.test(i)); //true
Long l = 1L;
System.out.println(numberClassPredicateForInstanceOf.test(l)); //true
System.out.println(numberClassPredicateForInstanceOf.test("1")); //false

In the above example, we create a predicate to test instanceOf for the Number type. Passing an instance of an integer will return true as an Integer is a Number. The result is the same when passing a Long (as a Long is a Number). But when passing a string instance, we get a false.

Violation of consistent with equals

The instanceOf violates the contract of Predicate#apply as the returned predicate may not be consistent with equals. The contract of Predicate#apply says for a given predicate, 

  • Two objects say a and b, and 
  • If they are equal (i.e., Objects.equals(a, b) = true), then
  • predicate.apply(a) must be the same as predicate.apply(b)

But with respect to instanceOf, this is not true. Example: We can have two instances of lists and they are equal i.e., have the same elements in the same order. But they may not be an instanceOf a particular type (a common parent).

List<Integer> list1 = List.of();
List<Integer> list2 = new ArrayList<>();
Predicate<Object> listClassPredicateForInstanceOf = Predicates.instanceOf(ArrayList.class);
System.out.println(listClassPredicateForInstanceOf.test(list1)); //false
System.out.println(listClassPredicateForInstanceOf.test(list2)); //true

We have created the first list using the List.of static factory method in the Collections class and the second list is an array list. Both the lists are equal as they are empty. But the first list is not an ArrayList.

subTypeOf

The subTypeOf method takes a class and returns a predicate which tests if the class being tested is assignable (i.e., is a subtype) of the class passed to the subTypeOf method. The predicate it returns is Predicate<Class<?>>.

Predicate<Class<?>> numberClassPredicateForSubTypeOf = Predicates.subtypeOf(Number.class);
System.out.println(numberClassPredicateForSubTypeOf.test(Number.class)); //true
System.out.println(numberClassPredicateForSubTypeOf.test(Integer.class)); //true
System.out.println(numberClassPredicateForSubTypeOf.test(Long.class)); //true
System.out.println(numberClassPredicateForSubTypeOf.test(String.class)); //false

We create a predicate to test subTypeOf for the Number type. When passing Number.class or Integer.class or Long.class, we get back true as these types are either the same or a subtype of Number.class. Passing String.class will return a false as String type doesn’t extend the Number class.

Contains and containsPattern

Predicates#contains

The contains method takes a Java Pattern and returns a Predicate<CharSequence> which tests if the CharSequence has any match for the given regular expression pattern.

In the below example, we have a regex to test if a string has only alphabets. We create a Pattern instance out of it and use it to create the predicate by calling the contains method. The returned predicate will return true only if the tested string has alphabets in it (lower or upper case).

Pattern hasOnlyAlphaBets = Pattern.compile("^[A-Za-z]+$");
Predicate<CharSequence> containsPredicate = Predicates.contains(hasOnlyAlphaBets);
System.out.println(containsPredicate.apply("java")); //true
System.out.println(containsPredicate.apply("Java")); //true
System.out.println(containsPredicate.apply("jAVA")); //true

System.out.println(containsPredicate.apply("12ab")); //false
System.out.println(containsPredicate.apply("ab12")); //false
System.out.println(containsPredicate.apply("12")); //false

In the first three tests, we pass strings having either lower and/or uppercase letters and hence the predicate returned true. If the string has anything other than the alphabets, it will return a false.

Predicates#containsPattern

The Google Guava Predicates containsPattern method is same as the contains but it takes the pattern as a String. It will throw an IllegalArgumentException if the passed pattern is invalid.

For this, I have the same example except we pass the regex as a string.

Predicate<CharSequence> containsPatternPredicate = Predicates.containsPattern("^[A-Za-z]+$");
System.out.println(containsPatternPredicate.apply("java")); //true
System.out.println(containsPatternPredicate.apply("Java")); //true
System.out.println(containsPatternPredicate.apply("jAVA")); //true

System.out.println(containsPatternPredicate.apply("12ab")); //false
System.out.println(containsPatternPredicate.apply("ab12")); //false
System.out.println(containsPatternPredicate.apply("12")); //false

Predicates – in

The Predicates#in takes a Collection<? extends T> and returns a Predicate<T>. The predicate will return true if the object we test for is present in the collection.

List<String> list = new ArrayList<>();
list.add("Java");
list.add("Google Guava");

Predicate<String> inPredicate = Predicates.in(list);
System.out.println(inPredicate.test("Java")); //true
System.out.println(inPredicate.test("Google Guava")); //true
System.out.println(inPredicate.test("Predicates")); //false

The first two calls to the test method returns true as the collection has the strings “Java” and “Google Guava” whereas the last call (“Predicates”) returns false because the collection doesn’t have it.

Collection changes

When we call the Predicates#in method, it doesn’t capture the collection contents by copying it. It just keeps a reference to the passed collection. Hence, any changes to the passed collection will change the predicate’s behaviour.

In the above example, if we add “Predicates” to the list, then the same call (which returned false earlier) will return a true.

System.out.println(inPredicate.test("Predicates")); //false

list.add("Predicates");
System.out.println(inPredicate.test("Predicates")); //true

Predicates – isNull and notNull

The isNull and notNull return predicates which tests if an object is null or not.

Predicate<String> isNull = Predicates.isNull();
System.out.println(isNull.test("a")); //false
System.out.println(isNull.test(null)); //true
 
Predicate<String> notNull = Predicates.notNull();
System.out.println(notNull.test("a")); //true
System.out.println(notNull.test(null)); //false

Predicates – equalTo

The equalTo method returns a predicate which checks if an object equals the passed target. We pass a value during the creation of the predicate. When we test with a value, it checks if the passed value equals the target used when creating the predicate.

Predicate<String> equalTo = Predicates.equalTo("value-1");
System.out.println(equalTo.test("value-1")); //true
System.out.println(equalTo.test("value-2")); //false

Predicates – compose

The compose method takes a Predicate and a Function. Its signature is,

public static <A, B> Predicate<A> compose(
    Predicate<B> predicate, Function<A, ? extends B> function) 

It composes the function with the predicate. For every input (of type A), it first invokes the function and then calls the predicate with the result the function returns.

Let us compose a function which takes a string and converts to an integer with a predicate to test if an integer is even.

Predicate<String> isEven = Predicates.compose(
        i -> i % 2 == 0,
        Integer::parseInt);

Integer::parseInt is a method reference for the lambda expression str -> Integer.parseInt(str).

Thus, for any passed string, first it converts it to an integer (hence the string must be a proper integer). Then, it checks if the integer is an even number or not.
System.out.println(isEven.test("2")); //true
System.out.println(isEven.test("4")); //true
System.out.println(isEven.test("5")); //false

Predicates – and, or and not

Predicates – and

There are three overloaded and() methods. 

  1. One takes two predicates
  2. One takes an var-args of predicates
  3. The last takes an Iterable of predicates

For the first version, it returns a predicate, which works by evaluating both the passed predicates for an input. It returns true if the evaluation of both the predicates is true. It “short-circuits” the evaluation, i.e., if the first predicate returns false, it will not check the other predicate.

Predicate<String> andPredicate = Predicates.and(s -> s.length() % 2 == 0,
        s -> s.startsWith("G"));
System.out.println(andPredicate.test("Google"));
System.out.println(andPredicate.test("Guava"));

Here, we have two predicates to

  • Check if a string is of even length.
  • Check if a string starts with the character ‘G’.

We create a new predicate by calling the and() with these two predicate. Thus, the returned predicate checks for both i.e., given a string, it must be of even length and must start with ‘G’.

System.out.println(andPredicate.test("Google")); //true
System.out.println(andPredicate.test("Guava")); //false
System.out.println(andPredicate.test("Java")); //false

For string “Google”, it returned true as it satisfies both the conditions. String “Guava” starts with a ‘G’, but its length is 5 and for string “Java” doesn’t start with a ‘G’.

Predicates#and with an Iterable

When we have a list of Predicates to check against, we can pass an Iterable of those predicates. It will check against each of the predicate in the Iterable in order. Again, it will “short-circuit” the check when it gets a false from one of the predicates.

However, there is a problem here. Since it takes an Iterable<? extends Predicate<? super T>> where Predicate is Google Guava’s Predicate. We cannot use a Java Predicate in our list (shown in below example). The Iterable can have either Google Guava Predicate or some type which is a subtype of it.

List<com.google.common.base.Predicate<String>> predicates = List.of(
        s -> s.length() % 2 == 0,
        s -> s.startsWith("G")
);

Predicate<String> andPredicate = Predicates.and(predicates);
System.out.println(andPredicate.test("Google")); //true
System.out.println(andPredicate.test("Guava")); //false
System.out.println(andPredicate.test("Java")); //false

Note: It will copy the passed Iterable and hence any changes to the Iterable will not affect the predicate and thus the check.

Predicates#and with an var-args

Similar to the above, but we can pass the predicates as a var-args. Even here, it makes a defensive copy of the passed input.

Predicates – or

We have the Predicates#or method to do a or with a set of predicates. Similar to the Predicates#and, there are three overloaded methods – for passing two predicates, for passing an iterable of predicates and var-args of predicates.

It will return a predicate which will evaluate to true if at least of the passed predicates returns a true. It will short-circuit the evaluation as soon as it finds a true predicate. And it will defensively copy the passed Iterable/var-args and hence any changes to them won’t affect the predicate.

Using the same set of predicates used before, if we combine them with the or(), then we will get a true for all as shown below.

Predicate<String> orPredicate = Predicates.or(s -> s.length() % 2 == 0,
        s -> s.startsWith("G"));
System.out.println(orPredicate.test("Google")); //true
System.out.println(orPredicate.test("Guava")); //true
System.out.println(orPredicate.test("Java")); //true

Using an Iterable,

List<com.google.common.base.Predicate<String>> predicates = List.of(
                s -> s.length() % 2 == 0,
                s -> s.startsWith("G")
        );
Predicate<String> orPredicate = Predicates.or(predicates);
System.out.println(orPredicate.test("Google")); //true
System.out.println(orPredicate.test("Guava")); //true
System.out.println(orPredicate.test("Java")); //true

Predicates – not

The not() takes a predicate and returns a predicate which negates the passed predicate. If the passed predicate evaluates to true, this will return a false and vice-versa.

Below, we pass a predicate to check if the string length is even to the not method. Hence, the notPredicate returns true for strings of odd length.

Predicate<String> notPredicate = Predicates.not(s -> s.length() % 2 == 0);
System.out.println(notPredicate.test("Google")); //false
System.out.println(notPredicate.test("Guava")); //true

Conclusion

In this post, we learnt about the Google Guava Predicates class in detail with examples. Check out the other google-guava posts here as well.
And make sure to follow me on Twitter and/or subscribe to my newsletter to get future post updates.

Leave a Reply