4.16. Modeling Loops with Closures

Problem

You need to execute a Closure multiple times.

Solution

Use a WhileClosure, passing in a Predicate and a Closure. The WhileClosure will execute the Closure as long as a Predicate evaluates to true. The following example demonstrates a Closure named drive, which operates on a Car object and a Predicate named hasFuel, which evaluates the Car object. Each time a Car is passed to drive, a gallon of fuel is used, and hasFuel will evaluate to true if the amount of fuel in a car is greater than zero. The WhileClosure, useAllFuel, evaluates drive until hasFuel evaluates to false:

import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.functors.WhileClosure;

Closure drive = new Closure( ) {
    public void execute(Object input) {
        Car car = (Car) input;
        car.setFuel( car.getFuel( ) - 1 );
    }
}

Predicate hasFuel = new Predicate( ) {
    public boolean evaluate(Object object) {
        Car car = (Car) input;
        return car.getFuel( ) > 0;
    }
}

Closure useAllFuel = new WhileFuel( hasFuel, drive );

Car car = new Car( );
car.setMakeModel( "Ford Escort" );
car.setFuel( 20 );
System.out.println( "Car before while closure: " + car );

useAllFuel.execute( car );
System.out.println( "Car after while closure: " + car );

The WhileClosure, useAllFuel, takes a Car object, executing a Closure and evaluating a Predicate after every execution. The state of the car is printed both before and after it is passed to the WhileClosure:

Car before while closure: Ford Escort with 20 gallons of fuel.
Car after while closure: Ford Escort with no fuel.

Discussion

If you need to execute a Closure a set number of times, you can also use a ForClosure, passing in an int that specifies the number of times an object is passed to the execute( ) method of a Closure. This example uses the same Closure defined in the Solution, but, this time, the drive Closure is only executed five times:

               Closure driveSome = new ForClosure( 5, drive );

Car car = new Car( );
car.setMakeModel( "Toyota Camry" );
car.setFuel( 20 );

System.out.println( "Car before for closure: " + car );

driveSome.execute( car );

System.out.println( "Car after for closure: " + car );

Since the driveSome Closure is called only five times, the Camry still has 15 gallons after the ForClosure is executed:

Car before for closure: Toyota Camry with 20 gallons of fuel.
Car after for closure: Toyota Camry with 15 gallons of fuel.

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.