4.7. Using Simple Predicates

Problem

You need to perform logic that is predicated on a certain condition being satisfied, and you want to encapsulate this condition in an object.

Solution

Use a Predicate to evaluate a criteria or a condition. A Predicate is an object that evaluates another object and returns true or false; predicates are used throughout the Commons Collections packages for filtering, selecting, and validating the contents of collections. This code demonstrates the use of simple predicates to test the type and contents of an object:

import org.apache.commons.collection.Predicate;
import org.apache.commons.collection.functors.*;

String name = "Tim";

Predicate nameJohn = new EqualPredicate( "John" );
Predicate nameTim = new EqualPredicate( "Tim" );

Predicate instanceString = new InstanceofPredicate( String.class );
Predicate instanceDouble = new InstanceofPredicate( Double.class );

// Testing all predicates for "Tim"
System.out.println( "Is Name John?: " + nameJohn.evaluate( name ) );
System.out.println( "Is Name Tim?: " + nameTim.evaluate( name ) );
System.out.println( "Is this a String?: " + instanceString.evaluate( name ) );
System.out.println( "Is this a Double?: " + instanceDouble.evaluate( name ) );

The previous example tests the name object against a few Predicate implementations producing the following console output:

Is Name John?: false
Is Name Tim?: true
Is this a String?: true
Is this a Double?: false

The string “Tim” is subjected to various Predicate tests. The first two EqualPredicate objects test the contents of the string, returning true if the object being evaluated is equal to the object passed into the EqualPredicate’s constructor. The last two Predicate objects are InstanceofPredicate instances, which test the type of object being evaluated; if an InstanceofPredicate constructor is passed to the String class, it returns true if the object being evaluated is a java.lang.String type.

Discussion

The simple Predicate interface is central to a number of utilities introduced in this chapter. To implement Predicate, define an evaluate() method that returns a boolean; a Predicate is a function object (or functor) that captures a criteria in an object that can be created and altered at runtime. Creating and evaluating a Predicate is just as valid as writing an if statement; for example, the code in the Solution of this recipe could have been implemented as a series of if statements:

String name = "Tim";

if( name.equals( "John" ) ) {
        System.out.println( "The name is John." );
}

if( name.equals( "Tim" ) ) {
        System.out.println( "The name is Tim." );
}

if( name instanceof String ) ) {
        System.out.println( "name is as String object" );
}

if( name instanceof Double ) ) {
        System.out.println( "name is as Double object" );
}

Predicate instances capture an if statement in an object, and if you are going to constantly change the behavior of your application, you might want to consider placing conditional expressions in Predicate instances. For example, if a system is designed to classify a storm as being a hurricane, you may want to capture all of your classification criteria in an XML file—parsing this file at runtime and creating a series of Predicate objects. A storm is a hurricane when the winds exceed a certain value, and the barometric pressure falls below a certain point. But, in a few years those criteria might change to involve a new, or more complex, set of measurements. If your decision logic is encapsulated in a Predicate object, it will be easier to upgrade the program to take new criteria into account; all of this logic will be encapsulated in an instance of Predicate.

Commons Collections provides a number of basic predicates for common situations, such as testing to see if an object equals another object (EqualPredicate), or that an object is of a certain type (InstanceofPredicate). Table 4-1 lists a number of simple Predicate implementations.

Table 4-1. Predicate implementations

Name

Description

EqualPredicate

Compares each object to an object passed via a constructor—returning true if the two are equal.

IdentityPredicate

Returns true if the object being evaluated is the same object reference as the object passed to its constructor. The IdentityPredicate uses the == operator to compare two object references.

NotPredicate

Wraps a Predicate and returns the opposite result.

InstanceOfPredicate

Returns true if the object being evaluated matches the type passed into its constructor.

NullPredicateNullIsTruePredicate

Returns true if the object being evaluated is null.

NotNullPredicateNullIsFalsePredicate

Returns true if the object being evaluated is not null.

TruePredicate

Always returns true.

FalsePredicate

Always returns false.

UniquePredicate

Returns true if it is the first time a particular object has been evaluated. The UniquePredicate maintains a HashSet of objects it has evaluated; if an object is already in that HashSet, this Predicate returns false. UniquePredicate can be used to select distinct objects from a collection.

The following example demonstrates simple Predicate objects with a test for equality, inequality, and equality by identity:

import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.functors.*;
...

String testName = "Ben";

Predicate equals = new EqualPredicate( testName );
Predicate notEquals = new NotPredicate( equals );
Predicate identity = new IdentityPredicate( testName );

System.out.println( "Does name equal 'Ben'? " + equals.evaluate( "Ben" ) );
System.out.println( "Is object 'Ben'? " + identity.evaluate( testName ) );
System.out.println( "Does name equal 'Tim'? " + equals.evaluate( "Tim" ) );
System.out.println( "Does name not equal 'Tim'? " + notEquals.
evaluate( "Tim" ) );
System.out.println( "Is object 'Tim'? " + identity.evaluate( "Tim" ) );

This code demonstrates the use of Predicate objects to determine if objects are equal or if two object references reference the same instance. When executed, the following is output to the console:

Does name equal 'Ben'? true
Is object 'Ben'? true
Does name equal 'Tim'? false
Does name not equal 'Tim'? true
Is object 'Tim'? false

The following code demonstrates simple predicates that test for the presence or absence of null or if an object being evaluated is of a certain type. The example also demonstrates the use of a UniquePredicate that returns true when it encounters an object for the first time:

import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.functors.*;

String nullString = null;
Double testDouble = new Double(3.4);

Predicate isString = new InstanceofPredicate( String.class );
Predicate isLong = new InstanceofPredicate( Long.class );
Predicate isNumber = new InstanceofPredicate( Number.class );

Predicate isNotNull = NotNullPredicate.INSTANCE;
Predicate isNull = NullPredicate.INSTANCE;

Predicate unique = new UniquePredicate( );

System.out.println("'nullString' not null?: " + isNotNull.
evaluate(nullString));
System.out.println("'nullString' null?: " + isNull.evaluate(nullString));

System.out.println("'testDouble' a String?: " + isString.
evaluate(testDouble));
System.out.println("'testDouble' a Long?: " + isLong.evaluate(testDouble));
System.out.println("'testDouble' a Number?: " + isNumber.
evaluate(testDouble));

System.out.println("'A' Unique?: " + unique.evaluate("A"));
System.out.println("'C' Unique?: " + unique.evaluate("C"));
System.out.println("'A' Unique?: " + unique.evaluate("A"));
System.out.println("'B' Unique?: " + unique.evaluate("B"));

The sample evaluates objects against the InstanceofPredicate, the NullPredicate, the NotNullPredicate, and the UniquePredicate, and the following is output to the console:

'nullString' not null?: false
'nullString' null?: true

'testDouble' a String?: false
'testDouble' a Long?: false
'testDouble' a Number?: true

'A' Unique?: true
'C' Unique?: true
'A' Unique?: false
'B' Unique?: true

The UniquePredicate returns false the second time it encounters “A.” The Double object testDouble is shown to be a Number object, and the nullString is evaluated as non-null.

See Also

This recipe mentions function pointers in C and C++. Function pointers are pointers to the address of a function, and they allow for some interesting logical acrobatics. For more information about function pointers in C and C++, see http://www.function-pointer.org/.

Predicate, Closure, and Transformer are all functor objects that are used throughout the Commons Collections component. You may be familiar with functors if you have used the Standard Template Library (STL) in C++. STL documentation contains rigorous definitions for function objects and predicates. For more information about functors in STL, see http://www.sgi.com/tech/stl/functors.html.

Get Jakarta Commons Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.