Skip to main content

Functional programming in the Java language

Use closures and higher order functions to write modular Java code

Abhijit Belapurkar (abhijit_belapurkar@infosys.com), Senior Technical Architect, Infosys Technologies Limited
Abhijit Belapurkar has a bachelor of technology degree in computer science from the Indian Institute of Technology (IIT), Delhi, India. He has worked in the areas of architectures and information security for distributed applications for more than 10 years and has used the Java platform to build n-tier applications for more than five years. He presently works as a senior technical architect in the J2EE space, with Infosys Technologies Limited, Bangalore, India.

Summary:  If you work on large-scale development projects, then you're familiar with the advantages of writing modular code. Well-structured, modular code is easier to write, debug, understand, and reuse. The problem for Java developers is that the functional programming paradigm has long been implemented only via specialized languages such as Haskell, Scheme, Erlang, and Lisp. In this article, author Abhijit Belapurkar shows you how to use functional programming constructs such as closures and higher order functions to write well-structured, modular code in the Java language. Editor's note: Since the publication of this article, Java developers have gained more options when it comes to functional programming. Read Ted Neward's series, "The busy Java developer's guide to Scala" for one excellent example.

Date:  13 Jul 2004
Level:  Introductory
Activity:  14457 views

One aspect of the Java language that is often overlooked is its classification as an imperative programming language. While fairly ubiquitous through its association with the Java language, imperative programming is not the only choice of programming style, nor is it always the most effective one. In this article, I will explore the potential of incorporating elements of a different programming methodology into your Java development practices; namely that of functional programming (FP).

Imperative programming is a methodology that describes computation in terms of program states. Programmers working in this paradigm use statements to change the program state. This is why, for example, a Java program consists of a sequence of commands (or statements) for the computer to perform. Functional programming, on the other hand, is a style of programming that emphasizes the evaluation of expressions rather than the execution of commands. Expressions are formed by using functions to combine basic values, which is akin to calling a function with arguments.

This article will introduce you to the basic characteristics of functional programming, but will focus on two elements that are especially applicable to the Java development framework: closures and higher order functions. If you've ever worked with an agile development language such as Python, Ruby, or Groovy (see Resources), then you've probably already encountered these elements. Here, you'll see what happens when you apply them directly within a Java development framework. I'll start with a short, conceptual overview of functional programming and its core elements, and then use common programming scenarios to demonstrate the benefits that a structured approach to using closures and higher order functions can bring to your Java code.

What is functional programming?

In the often quoted paper "Why Functional Programming Matters" (see Resources) author John Hughes makes the case that modularity is the key to successful programming, and that functional programming allows greatly improved modularization. In functional programming, the programmer is given a natural framework for developing smaller, simpler, and more general modules, which are then glued together. Some of the basic characteristics of functional programming include:

  • Support for closures and higher order functions
  • Support for lazy evaluation
  • Use of recursion as a mechanism for control flow
  • Enforcement of referential transparency
  • No side-effects

I will focus on the use of closures and higher order functions in the Java language, but start with an overview of all the characteristics listed above.

Closures and higher order functions

Functional programming supports functions as first-class objects, sometimes called closures, or functor objects. Essentially, closures are objects that act as functions and can be operated upon as objects. Similarly, FP languages support higher order functions. A higher order function is able to take another function (indirectly, an expression) as its input argument, and in some cases it may even return a function as its output argument. These two constructs together allow for elegant ways to modularize programs, which is one of the biggest advantages of using FP.

Imperative programming

The name imperative programming is derived from the imperative mood of natural languages such as English, where a command is asserted and then acted upon. Aside from the Java language, C and C++ are the two other widely used high-level programming languages that conform to the imperative style.

Lazy evaluation

Aside from the notion of higher order functions and functors (or closures), FP also introduces the concept of lazy evaluation. In lazy evaluation, an expression is not evaluated as soon as it is bound to a variable, but when the evaluator is forced to produce the expression's value. Delayed evaluation lets you write functions that can potentially generate infinite output. Because no more values will be computed than the rest of the program requires, you need not worry about out-of-memory errors caused by infinite computation. One example of a lazy evaluation is a function that generates an infinite list of Fibonacci numbers, but where the computation of the nth Fibonacci number is equivalent to extracting just one item from that potentially infinite list.

Recursion

FP is also characterized by its use of recursion as a mechanism for control flow. For example, Lisp works with lists that are defined as a head element followed by a sub-list, a notation that lends itself naturally to recursion on progressively smaller sub-lists.

About the implementation library

I've used the implementation library provided by the Apache Commons Functor project to build the examples used in this article. The Apache Commons Functor libraries comprise a vast collection of basic constructs that can be re-used in complex usage scenarios involving closures and higher order functions. Of course, different implementations (such as the Java Generic Libraries, Mango, or Generic Algorithms for Java) can be used with no impact on the concepts discussed and demonstrated in this article, although you will have to download and use the Apache Commons Functor library to follow the examples here.

Referential transparency

Functional programs also typically enforce referential transparency, in that a function always returns the same result if it is provided the same input. That is, the value of the expression does not depend on global state, which can change value. This allows you to formally reason about program behavior, because the meaning of an expression depends only on the meaning of its subexpressions and not on the order of evaluation or side-effects of other expressions. This can help in proving correctness, simplifying an algorithm, or even finding ways of optimizing it.

Side-effects

A side-effect is a language construct that modifies the state of the system. Because FP languages do not contain any assignment statements, values of variables, once assigned, never change. Also, a function being called results only in the computation of an outcome -- no other effect can possibly occur. Thus, FP languages have no side-effects.

This basic outline should be enough to carry you through the functional programming examples in this article. See the Resources section for further references on this topic.


Functional programming in the Java language

Believe it or not, you've probably already encountered closures and higher order functions in your Java development practices, although you may not have realized it at the time. For example, many Java developers first encounter closures when they enclose a lexical unit of Java code within an anonymous inner class for execution. This enclosed unit of Java code is executed, on demand, by a higher order function. For example, the Java code in Listing 1 encloses a method call within an object of the type java.lang.Runnable.


Listing 1. A closure in disguise
Runnable worker = new Runnable()
{
  public void run()
  {
    parseData(); 
  }
 };

The method parseData is literally enclosed (hence the name "closure") within the Runnable object instance, worker. It can be passed around between methods as if it were data and can be executed at any time by sending a message (called run) to the worker object.

Further examples

Another example of the usage of closures and higher order functions in an object-oriented world is the Visitor pattern. If you're not already familiar with this pattern, see Resources to learn more about it. Basically, the Visitor pattern presents a participant called the Visitor, whose instances are accepted by a composite object (or data structure) and applied to each of the constituent nodes of that data structure. The Visitor object essentially encloses the logic to process a node/element, using the accept (visitor) method of the data structure as the higher order function to apply the logic.

A completely different processing logic can be applied to the elements of the data structure by using an appropriately different Visitor object (that is, a closure). Similarly, you could pass the same closure to a different higher order function to process the data structure another way (for example, this new higher order function could implement a different logic for iterating over the constituent elements).

Another example is provided by the class java.utils.Collections that has been part of the Java 2 SDK since version 1.2. One of the utility methods it provides is a method to sort the elements contained within a java.util.List. However, it allows the caller to encapsulate the logic for ordering the elements of the list in an object of type java.util.Comparator, with the Comparator object being passed as the second argument to the sort method.

Internally, the reference implementation of the sort method is based on the merge-sort algorithm. It iterates over the elements in the list by calling the compare method of the Comparator object on adjacent objects in order. In this situation, the Comparator is a closure, while the Collections.sort method is a higher order function. The advantage of this approach is clear: you can pass in different Comparator objects depending on the type of objects contained in the list (because the logic for how to go about comparing any two objects in the list is safely encapsulated in the Comparator object). Similarly, another implementation of the sort method could use a completely different algorithm (say, quick-sort) and yet re-use the same Comparator object, applying it as the basic function on a certain combination of two elements in the list.


Creating closures

Broadly speaking, there are two techniques for generating closures, each of which can be equally applied by the code that will use the closure. Once you've created a closure you can pass it around in a uniform manner and also send it messages to get it to execute its enclosing logic. The choice of technique is, therefore, a matter of preference, and in some cases circumstance.

Last call for download!

The discussion from here forward will incorporate examples based on the Apache Commons Functor library. If you haven't already downloaded the library, you should do so now. I will assume that you have access to the Javadocs accompanying the Apache library, and so will explain the individual library classes only so far as absolutely necessary.

In the first technique, expression specialization, a general interface for the closure is provided by the infrastructure and a concrete closure is created by writing a specialized implementation of the interface. In the second technique, expression composition, the infrastructure provides concrete helper classes that implement fundamental unary/binary/ternary/.../n-ary operations (such as not, a unary operator, and and / or , both binary operators etc.). In this case, new closures are created as arbitrary compositions formed out of these basic building blocks.

I'll discuss each of these techniques in detail in the sections that follow.


Expression specialization

Suppose you are writing an application for an online store. Items available in the store are modeled by a class SETLItem. Each item has a marked price associated with it; the SETLItem class provides a method called getPrice that returns the marked price of the item instance on which the method was called.

How do you check whether item1 costs at least as much as item2? In the Java language, you'd typically write an expression like this:

assert(item1.getPrice() >= item2.getPrice());

An expression like this one is called a binary predicate; binary because it takes two arguments and predicate because it does something using those two arguments and produces a boolean result. Note, however, that the expression above can only be executed when it is encountered in the execution flow and its outcome depends on the values of item1 and item2 at a particular instant. From a functional programming standpoint, the expression is not yet a general piece of logic; that is, it cannot be passed around and asked to execute whenever you want, without regard to the current position of execution control.

In order to make your binary predicate functional, you must enclose it in an object; you will do this by specializing an interface called BinaryPredicate, which is provided to you by the Apache Functor library, as shown in Listing 2.


Listing 2. The expression specialization approach
package com.infosys.setl.fp;
public class SETLItem
{
  private String name;
  private String code;
  private int price;
  private String category;
		
  public int getPrice()
  {
	return price;
  }
	
  public void setPrice(int inPrice)
  {
	price = inPrice;
  }
	
  public String getName()
  {
	return name;
  }
	
  public void setName(String inName)
  {
	name = inName;
  }

  public String getCode()
  {
	return code;
  }
	
  public void setCode(String inCode)
  {
	code = inCode;
  }
	
  public String getCategory()
  {
	return category;
  }
	
  public void setCategory(String inCategory)
  {
	category = inCategory;
  }
}


package com.infosys.setl.fp;
import java.util.Comparator;
public class PriceComparator implements Comparator
{
  public int compare (Object o1, Object o2)
  {
	return (((SETLItem)o1).getPrice()-((SETLItem)o2).getPrice());
  }	
}


package com.infosys.setl.fp;
import org.apache.commons.functor.*;
import org.apache.commons.functor.core.comparator.*;
import java.util.*;
public class TestA
{
  public static void main(String[] args)
  {
	try
	{
	  Comparator pc = new PriceComparator();
	  BinaryPredicate bp = new IsGreaterThanOrEqual(pc);
	  SETLItem item1 = new SETLItem();
	  item1.setPrice(100);
	  SETLItem item2 = new SETLItem();
	  item2.setPrice(99);
	  if (bp.test(item1, item2))
	    System.out.println("Item1 costs more than Item2!");
	  else
	    System.out.println("Item2 costs more than Item1!");

	  SETLItem item3 = new SETLItem();
	  item3.setPrice(101);
	  if (bp.test(item1, item3))
	    System.out.println("Item1 costs more than Item3!");
	  else
  	    System.out.println("Item3 costs more than Item1!");
	}
	catch (Exception e)
	{
	  e.printStackTrace();
	}
  }
}

The BinaryPredicate interface has been specialized in the form of the IsGreaterThanOrEqual class provided by the Apache Functor library. The PriceComparator class, which implements the java.util.Comparator interface, is passed as input to the IsGreaterThanOrEqual class. The IsGreaterThanOrEqual class automatically calls the compare method of the PriceComparator class upon being sent a test message. The compare method expects to be passed two SETLItem objects; in turn it will return the difference between the prices of the two items. A positive response from the compare method is interpreted as item1 costing at least as much as item2.

At first glance this seems like a lot of work for a fairly basic operation, so what did it buy you? Specializing the BinaryPredicate interface (rather than writing a Java comparison expression) sets you up to be able to compare the prices of any two items -- whenever and wherever you want to. You can pass the bp object around as data and send it a message to execute (called test) anytime you like, using whatever values you like for the two arguments.


Expression composition

Expression composition is a slightly different technique that yields the same results. Consider the problem of computing the net price of a specific SETLItem, taking into account the current discount and sales tax rates. The source code for the functor-based approach to solving this problem is presented in Listing 3.


Listing 3. The expression composition approach
package com.infosys.setl.fp;
import org.apache.commons.functor.BinaryFunction;
import org.apache.commons.functor.UnaryFunction;
import org.apache.commons.functor.adapter.LeftBoundFunction;
public class Multiply implements BinaryFunction
{
  public Object evaluate(Object left, Object right) 
  {
	return new Double(((Double)left).doubleValue() * ((Double)right).doubleValue());
  }
}


package com.infosys.setl.fp;
import org.apache.commons.functor.*;
import org.apache.commons.functor.core.composite.*;
import org.apache.commons.functor.adapter.*;
import org.apache.commons.functor.UnaryFunction;
public class TestB
{
  public static void main(String[] args)
  {
	try
	{
  	  double discountRate = 0.1;
	  double taxRate=0.33;
	  SETLItem item = new SETLItem();
	  item.setPrice(100);
	  UnaryFunction calcDiscountedPrice = 
	  new RightBoundFunction(new Multiply(), new Double(1-discountRate));
	  UnaryFunction calcTax = 
	  new RightBoundFunction(new Multiply(), new Double(1+taxRate));
	  CompositeUnaryFunction calcNetPrice = 
	  new CompositeUnaryFunction(calcTax, calcDiscountedPrice);
	  Double netPrice = (Double)calcNetPrice.evaluate(new Double(item.getPrice()));
	  System.out.println("The net price is: " + netPrice); 
	}
	catch (Exception e)
	{
	  e.printStackTrace();
	}
  }
}

A BinaryFunction is, similar to the BinaryPredicate that you saw earlier, a generalized functor interface provided by the Apache Functor library. The BinaryFunction interface takes two arguments and returns an Object value. Similarly, a UnaryFunction is a functor interface that takes one Object argument and returns an Object value.

A RightBoundFunction is an adapter class provided by the Apache library that adapts a BinaryFunction to the UnaryFunction interface, by using a constant right-side argument. That is, upon receipt of the appropriate message (evaluate) with a single argument, it internally sends an evaluate message to the BinaryFunction it is adapting with two arguments -- the left one being the single argument that was sent to it and the right one being the constant that it knows about. As you must have guessed, the name RightBoundFunction comes from the fact that the constant value is passed as the second (right) argument. (Yes, the Apache library also provides a LeftBoundFunction in which the constant is passed as the first, or left, argument.)

A specialized BinaryFunction for multiplying Doubles

Listing 3 shows a specialized BinaryFunction called Multiply that takes two Doubles as input and returns a new Double formed out of multiplying the former two together.

A new RightBoundFunction is instantiated in calcDiscountedRate that adapts the binary Multiply function to a unary interface by using (1 - discountRate) as its constant second argument.

As a result, you can send a message called evaluate to the calcDiscountRate with a single Double argument. Internally, the input Double argument is then multiplied with the constant value being "held" within the calcDiscountRate object itself.

Similarly, a new RightBoundFunction is instantiated in calcTaxRate that adapts the binary Multiply function to a unary interface by using (1 + taxRate) as its constant second argument. As a result, you can send a message called evaluate to the calcTaxRate with a single Double argument. Internally, the input Double argument is then multiplied, with the constant value being "held" within the calcTaxRate object itself.

This technique of rewriting a function with multiple parameters as the composition of functions of one parameter is also known as currying.

The composition magic plays its part in the last bit. Essentially, the algorithm to compute the net price of the object is to first calculate the discounted price (using the calcDiscountRate functor) and then calculate the net price by including the sales tax on top of it (using the calcSalesTax functor). That is, you need to compose a function that internally calls the first functor and streams the output of that evaluation as input into the evaluation of the second functor. The Apache library provides a built-in functor for this purpose, called the CompositeUnaryFunction.

In Listing 3, the CompositeUnaryFunction is instantiated into the variable calcNetPrice as a composition of the calcDiscountRate and calcSalesTax functors. As before, you will be able to pass this object around to others, who can also ask it to compute the net price of an item by sending an evaluate message to it that includes the said item as its argument.

Unary vs. binary composition

In Listing 3 you saw an example of unary composition, where the result of one unary functor was input to another. Another type of composition is called binary composition, where the results of two unary functors are passed, as part of an evaluate message, as parameters to a binary functor.

Listing 4 is an example of both the necessity and style of binary composition. Suppose that you want to ensure that the maximum discount that your store can give is capped by an upper limit. Therefore, you must compare the discount amount obtained as the evaluation outcome of the calcDiscount functor with the cap value and take the minimum of the two in computing the discounted price. The discounted price is computed by subtracting the actual discount from the marked price.


Listing 4. A binary composition
package com.infosys.setl.fp;
import org.apache.commons.functor.BinaryFunction;
public class Subtract implements BinaryFunction
{
  public Object evaluate(Object left, Object right) 
  {
	return new Double(((Double)left).doubleValue() - ((Double)right).doubleValue());
  }
}


package com.infosys.setl.fp;
import org.apache.commons.functor.BinaryFunction;
import org.apache.commons.functor.UnaryFunction;
public class BinaryFunctionUnaryFunction implements UnaryFunction
{
  private BinaryFunction function;
	
  public BinaryFunctionUnaryFunction(BinaryFunction f)
  {
	function=f;	
  }
	
  public Object evaluate(Object obj) 
  {
	return function.evaluate(obj,obj);
  }
}



package com.infosys.setl.fp;
import org.apache.commons.functor.*;
import org.apache.commons.functor.core.composite.*;
import org.apache.commons.functor.adapter.*;
import org.apache.commons.functor.UnaryFunction;
import org.apache.commons.functor.core.Constant;
import org.apache.commons.functor.core.comparator.Min;
public class TestC
{
  public static void main(String[] args)
  {
	double discountRate = 0.1;
	double taxRate=0.33;
	double maxDiscount = 30;
	SETLItem item = new SETLItem();
	item.setPrice(350);
	UnaryFunction calcDiscount = 
	  new RightBoundFunction(new Multiply(), new Double(discountRate));
	Constant cap = new Constant(new Double(maxDiscount));
	BinaryFunction calcActualDiscount = 
	  new UnaryCompositeBinaryFunction (new Min(), calcDiscount, cap);
	BinaryFunctionUnaryFunction calcActualDiscountAsUnary = 
	  new BinaryFunctionUnaryFunction(calcActualDiscount);
	BinaryFunction calcDiscountedPrice = 
	  new UnaryCompositeBinaryFunction (new Subtract(), new Identity(), calcActualDiscountAsUnary);
	BinaryFunctionUnaryFunction calcDiscountedPriceAsUnary = 
	  new BinaryFunctionUnaryFunction(calcDiscountedPrice);	
	UnaryFunction calcTax = 
	  new RightBoundFunction(new Multiply(), new Double(1+taxRate));
	CompositeUnaryFunction calcNetPrice = 
	  new CompositeUnaryFunction(calcTax, calcDiscountedPriceAsUnary);
	Double netPrice = (Double)calcNetPrice.evaluate(new Double(item.getPrice()));
	System.out.println("The net price is: " + netPrice);
  }
}

You can begin unpacking and understanding this code by first looking at the three standard functors you've used from the Apache Functors library.

  • The UnaryCompositeBinaryFunction functor takes a binary function and two unary functions as input. The latter two are evaluated first and their outputs are passed in as inputs to the binary function. You used this functor twice for binary composition in Listing 4.

  • The Constant functor's evaluation always returns a constant value (whatever was input during its construction), irrespective of the argument passed in with any evaluate message that may be sent to it at a later time. In Listing 4, the variable cap is of type Constant and will always return the maximum discount amount.

  • The Identity functor simply returns as output the same object passed to it as the input argument to an evaluate message. Listing 4 shows an instance of the Identity functor being created and passed as one of the unary functors during the creation of the calcDiscountedPrice. Also in Listing 4, the evaluate message contains the marked price as its argument, so the Identity functor returns the marked price as output.

The first binary composition is visible when you set the variable calcActualDiscount with a UnaryCompositeBinaryFunction that evaluates calcDiscount (by directly applying the discount rate on the marked price) and cap. The outputs from the evaluations of these two unary functors are passed to a built-in binary functor called Min that compares the two and returns the smaller of their two values.

This example shows the custom class BinaryFunctionUnaryFunction. This class adapts a binary functor so that it has an interface like that of a unary functor. That is, when this class receives an evaluate message with a single argument, it internally sends (to its enclosed binary Function) an evaluate message whose two arguments are the same object that it received as input. Because calcActualDiscount is a binary function, you wrap it inside a unary functor interface via an instance, calcActualDiscountAsUnary, of the type BinaryFunctionUnaryFunction. The reason for wrapping calcActualDiscount as a unary functor will be clear in a moment.

The second binary composition occurs when the variable calcDiscountedPrice is set with a UnaryCompositeBinaryFunction that sends evaluation messages to a new Identity instance and to the calcActualDiscountAsUnary object, with the marked price being the input argument for both messages.

The outputs of these two evaluations (which work out to be the marked price and actual discount amount, respectively) are passed to a custom binary functor called Subtract. As soon as this latter object is sent the evaluate message, it computes and returns the difference between the two arguments (which works out to be the discounted price of the item). You also warp this binary functor as a unary functor object called calcDiscountedPriceAsUnary, using your custom BinaryFunctionUnaryFunction.

As in the previous case, the code finishes with a bit of unary composition by creating a CompositeUnaryFunction with the two unary functors calcTax (which you also encountered in Listing 3) and the calcDiscountedPriceAsUnary (which was described in the previous paragraph). The calcNetPrice thus obtained is geared to accept an evaluate message with a single argument (the marked price of the concerned item) and, internally, first evaluate the calcDiscountedPriceAsUnary functor with this argument and then evaluate the calcTax functor passing in the outcome of the previous evaluation as parameter to this one.


Using closures to implement business rules

The Apache Library provides a wide variety of built-in unary and binary functors that make it very easy to write business rules as objects that can be passed around and executed at different places with different arguments. As in the previous sections, I'll use a simple example to demonstrate the functional programming approach to a familiar problem.

Suppose that whether or not a particular item is eligible for discount depends on the category and the list price of the item. Specifically, only Category "A" items with a list price of more than $100 and Category "B" items with a list price of more that $200 are eligible for discounts. The code in Listing 5 shows a business rule object (a UnaryPredicate) called isEligibleForDiscount that, upon being sent an evaluate message with an item object as argument, returns a Boolean indicating whether or not the discount should be applied to it.


Listing 5. A functional business rule object
package com.infosys.setl.fp;
import org.apache.commons.functor.BinaryPredicate;
import org.apache.commons.functor.UnaryPredicate;
public class BinaryPredicateUnaryPredicate implements UnaryPredicate
{
  private BinaryPredicate bp;
	
  public BinaryPredicateUnaryPredicate(BinaryPredicate prd)
  {
	bp=prd;	
  }
	
  public boolean test(Object obj)
  {
	return bp.test(obj,obj);
  }
}


package com.infosys.setl.fp;
import org.apache.commons.functor.*;
import org.apache.commons.functor.core.composite.*;
import org.apache.commons.functor.adapter.*;
import org.apache.commons.functor.UnaryFunction;
import org.apache.commons.functor.core.Constant;
import org.apache.commons.functor.core.IsEqual;
import org.apache.commons.functor.core.comparator.IsGreaterThanOrEqual;
import org.apache.commons.functor.core.comparator.Min;
import org.apache.commons.functor.core.Identity;
public class TestD
{
  public static void main(String[] args)
  {		
	SETLItem item1 = new SETLItem();
	item1.setPrice(350);
	item1.setCategory("A");

	SETLItem item2 = new SETLItem();
	item2.setPrice(50);
	item2.setCategory("A");

	SETLItem item3 = new SETLItem();
	item3.setPrice(200);
	item3.setCategory("B");
		
	UnaryFunction getItemCat = 
	new UnaryFunction()
	{
	public Object evaluate (Object obj)
	{
		return ((SETLItem)obj).getCategory();
}
  	 };
  	 
	UnaryFunction getItemPrice = 
	new UnaryFunction()
	{
	public Object evaluate  (Object obj)
	{
		return new Double(((SETLItem)obj).getPrice());
}
	 };
		
	Constant catA = new Constant("A");
	Constant catB = new Constant("B");
	Constant usd100 = new Constant(new Double(100));
	Constant usd200 = new Constant(new Double(200));
		
	BinaryPredicateUnaryPredicate belongsToCatA = new BinaryPredicateUnaryPredicate
	  (new UnaryCompositeBinaryPredicate(new IsEqual(), getItemCat, catA));
	BinaryPredicateUnaryPredicate belongsToCatB = new BinaryPredicateUnaryPredicate
	  (new UnaryCompositeBinaryPredicate(new IsEqual(), getItemCat, catB));
		
	BinaryPredicateUnaryPredicate moreThanUSD100 = new BinaryPredicateUnaryPredicate
	  (new UnaryCompositeBinaryPredicate(new IsGreaterThanOrEqual(), getItemPrice, usd100));
	BinaryPredicateUnaryPredicate moreThanUSD200 = new BinaryPredicateUnaryPredicate
	  (new UnaryCompositeBinaryPredicate(new IsGreaterThanOrEqual(), getItemPrice, usd200));
		
	UnaryOr isEligibleForDiscount = new UnaryOr(new UnaryAnd(belongsToCatA, moreThanUSD100), 
	  new UnaryAnd(belongsToCatB, moreThanUSD200));

	if (isEligibleForDiscount.test(item1))
	  System.out.println("Item #1 is eligible for discount!");
	else
	  System.out.println("Item #1 is not eligible for discount!");
			
	if (isEligibleForDiscount.test(item2))
	  System.out.println("Item #2 is eligible for discount!");
	else
	  System.out.println("Item #2 is not eligible for discount!");

	if (isEligibleForDiscount.test(item3))
	  System.out.println("Item #3 is eligible for discount!");
	else
	  System.out.println("Item #3 is not eligible for discount!");		 
  }
}

Using the ComparableComparator

The first thing you'll likely notice in Listing 5 is that I've made use of built-in binary predicate functors called isEqual (to check whether the category of the concerned item equals "A" or "B") and isGreaterThanOrEqual (to check whether the list price of the concerned item is greater than or equal to a specific value -- 100 in the case of Category "A" items and 200 in the case of Category "B" items).

As you may recall from Listing 2, you previously had to pass in a PriceComparator object (encapsulating the comparison logic) in order to use the isGreaterThanOrEqual functor for a price comparison. In Listing 5, however, you did not pass this Comparator object explicitly. How did you get away with that? The trick is that the isGreaterThanOrEqual functor (and even the IsEqual functor, for that matter) uses a default ComparableComparator if you don't specify one. This default Comparator assumes that the two objects to be compared implement the java.lang.Comparable interface and simply calls the compareTo method on the first argument (after typecasting it as Comparable), passing the second argument as a parameter to this method.

By simply delegating the comparison effort to the object itself, the default Comparator remains usable as-is for both String comparisons (like you did for the item categories) and Double comparisons (like you did for the item prices); both String and Double are default Java types that implement the Comparable interface.

Adapting a binary predicate as unary

The second thing you may note is that I have introduced a new functor called BinaryPredicateUnaryPredicate. This functor (similar to the BinaryFunctionUnaryFunction functor first encountered in Listing 4) adapts a binary predicate interface to a unary one. The BinaryPredicateUnaryPredicate functor can be thought of as a unary predicate with a single argument: it internally evaluates the wrapped binary predicate with two copies of the same argument.

The isEligibleForDiscount object encapsulates one complete business rule. As can be seen, the manner in which it has been constructed -- that is, from the ground up, by putting together basic blocks to form slightly more complex blocks, putting those together to form even more complex blocks, and so on -- lends itself naturally to some kind of a "visual" rule builder. The final rule object can be an arbitrarily complex expression which can be constructed on-the-fly and then passed around to evaluate the underlying business rule.


Operating on collections

The GoF Iterator pattern (see Resources) provides a way to access the elements of an aggregate object without exposing its underlying representation. The philosophy behind this approach is that the iteration is decoupled from the data structure (that is, it isn't part of the collection). The approach itself depends on using an object that represents a specific location within a collection, along with a loop condition (one that increments its logical position within the collection) to traverse all the elements in the collection. The rest of the instructions in the loop body can then examine and/or operate on the specific item in the collection located at the current Iterator object position. In this case, you have little control over the iteration. (For example, in terms of how many times next must be called, every time you try to access the next element you must first check for out-of-range errors.) In addition, the iterator must use the same public interface as everybody else to access the "members" of the underlying data structure, which makes access inefficient. This kind of iterator is often referred to as "External Iterator."

FP takes a very different approach to this problem. The collection class has a higher order function that takes a functor as parameter and internally applies it to every member of the collection. In this case, because the iterator shares the implementation of the data structure, you have complete control over the iteration. In addition, the iteration is fast because it has direct access to the data structure members. This kind of iterator is often referred to as an internal Iterator.

The Apache Functor library provides a variation of the internal Iterator loosely based on the C++ Standard Template Library implementation. It provides a utility class called Algorithms with a method called foreach. The foreach method accepts an Iterator object and a unary Procedure as input and runs the procedure once for each element encountered during the traversal over the Iterator (the element itself is passed as the single parameter to the procedure).

Using internal Iterators

A simple example will clarify the difference between external and internal Iterators. Suppose that you are provided with a list of SETLItem objects and asked to add up the list prices of only those items in the list that cost more than $200. The sample code for doing this is presented in Listing 6.


Listing 6. Using external and internal Iterators
 package com.infosys.setl.fp;
import java.util.*;
import org.apache.commons.functor.Algorithms;
import org.apache.commons.functor.UnaryFunction;
import org.apache.commons.functor.UnaryProcedure;
import org.apache.commons.functor.core.Constant;
import org.apache.commons.functor.core.collection.FilteredIterator;
import org.apache.commons.functor.core.comparator.IsGreaterThanOrEqual;
import org.apache.commons.functor.core.composite.UnaryCompositeBinaryPredicate;
public class TestE
{
  public static void main(String[] args)
  {		
	Vector items = new Vector();
	for (int i=0; i<10; i++)
	{
  	  SETLItem item = new SETLItem();
	  if (i%2==0)
		item.setPrice(101);
	  else
		item.setPrice(i);
	  items.add(item);
	}
	TestE t = new TestE();	
	System.out.println("The sum calculated using External Iterator is: " + 
	  t.calcPriceExternalIterator(items));
	System.out.println("The sum calculated using Internal Iterator is: " +
	   t.calcPriceInternalIterator(items));
  }
	
  public int calcPriceExternalIterator(List items)
  {
	int runningSum = 0;
	Iterator i = items.iterator();
	while (i.hasNext())
	{
  	  int itemPrice = ((SETLItem)i.next()).getPrice();
	  if (itemPrice >= 100)
		runningSum += itemPrice;
	}
	return runningSum;
  }
	
  public int calcPriceInternalIterator(List items)
  {
	Iterator i = items.iterator();
	UnaryFunction getItemPrice = 
	new UnaryFunction()
	{
	public Object evaluate (Object obj)
	{
		return new Double(((SETLItem)obj).getPrice());
}
	};
	Constant usd100 = new Constant(new Double(100));
	BinaryPredicateUnaryPredicate moreThanUSD100 = new BinaryPredicateUnaryPredicate
	  (new UnaryCompositeBinaryPredicate(new IsGreaterThanOrEqual(), getItemPrice, usd100));
	FilteredIterator fi = new FilteredIterator(i, moreThanUSD100);
	Summer addPrice = new Summer();
	Algorithms.foreach(fi, addPrice);
	return addPrice.getSum();
  }
	
  static class Summer implements UnaryProcedure
  {
	private int sum=0;
	public void run(Object obj)
	{
	  sum += ((SETLItem)obj).getPrice();
	}
	public int getSum()
	{
	  return sum;			
	}
  }
}

In the main() method, you set up a list of 10 items in which the even-numbered elements are priced at $101. (In a real application, you would be working with a ResultSet obtained as the outcome of a JDBC call instead of a Vector, as in this example.)

You then perform the desired operation on the list using two different methods: calcPriceExternalIterator and calcPriceInternalIterator. As the names would indicate, the former is based on an ExternalIterator and the latter is based on an InternalIterator. You'll concern yourself with the latter method, as the former should be familiar to any Java developer. Note that the InternalIterator method uses two constructs provided by the Apache Functor library.

The first construct is called FilteredIterator. It takes an iterator along with a unary Predicate as input and returns an Iterator with an interesting property. This property says that every element you encounter while traversing the Iterator satisfies the condition codified in the Predicate. (The Iterator returned by one instance of the FilteredIterator could therefore be passed as input to a second instance of FilteredIterator, and so on, to set up a chain of filters that successively strain out elements based on a multitude of criteria.) In this case, you are interested only in items that satisfy the unary Predicate "is Price greater than or equal to $100" rule. This rule is codified in the BinaryPredicateUnaryPredicate called moreThanUSD100, which you first encountered in Listing 5.

The second construct provided by the Apache Functor library is the utility class called Algorithms, which was previously described. In the example, the unary procedure called Summer simply obtains the list price of the SETLItem instance passed to it and adds it to a (local) running total variable. This is a classic implementation of the internal iterator concept, as previously discussed.


Set manipulation using functors

I've covered a lot of ground in writing modular code using functors and higher order functions. I'll conclude with a final example that demonstrates how set manipulation operations can be implemented using functors.

Typically, there are two ways of describing the membership of a set. The first is to exhaustively list all the elements in the set. This is the mechanism that is traditionally available to you as a Java programmer -- the java.util.Set interface provides a method called add(Object) that adds the object passed in as argument to the underlying collection if it is not already present.

When the elements in a set share some common properties, however, it can be more effective to describe the membership of the set by stating the properties that uniquely characterize the elements in the set. This latter solution is suited, for example, to scenarios where the number of possible set members is so large that it is not feasible to explicitly maintain a set implementation (per the previous approach) in memory.

In such cases, a unary Predicate can be used to denote the set. As should be obvious, a unary Predicate implicitly defines a set of all values (objects) that lead to the predicate evaluating to true. In fact, all set operations can be defined in terms of different types of predicate compositions. This is shown in Listing 7:


Listing 7. Set operations using functors
package com.infosys.setl.fp;
import org.apache.commons.functor.UnaryPredicate;
import org.apache.commons.functor.core.composite.UnaryAnd;
import org.apache.commons.functor.core.composite.UnaryNot;
import org.apache.commons.functor.core.composite.UnaryOr;
public class SetOps
{
  public static UnaryPredicate union(UnaryPredicate up1, UnaryPredicate up2)
  {
	return new UnaryOr(up1, up2);
  }
	
  public static UnaryPredicate intersection(UnaryPredicate up1, UnaryPredicate up2)
  {
	return new UnaryAnd(up1, up2);
  }
	
  public static UnaryPredicate difference(UnaryPredicate up1, UnaryPredicate up2)
  {
	return new UnaryAnd(up1, new UnaryNot(up2));
  }
	
  public static UnaryPredicate symmetricDifference(UnaryPredicate up1, 
    UnaryPredicate up2)
  {
	return difference(union(up1, up2), intersection(up1, up2));
  }
}

The definitions of set union and intersection operations in terms of unary Predicates should be clear: an object belongs to the union of two sets if it causes at least one of the two unary Predicates denoting the two sets to evaluate as true (logical Or). It belongs to the intersection of two sets if it causes both the unary Predicates to evaluate as true (logical And).

The difference between two sets is mathematically defined as the set of elements that belong to the first set but not to the second. From this definition, the static method difference is also easy to understand.

Lastly, the symmetric difference between two sets is defined as the set of all elements that belong to only one of the two sets (and not to both). This can be worked out to taking the union of the two sets and then removing from it the elements belonging to the intersection of the two sets. That is, it is the difference operation applied on the two sets obtained by applying the union and intersection operations, respectively, on the original sets (unary Predicates, in this case). This latter definition should clarify how the first three methods have been used as building blocks within the fourth method.


Conclusion

It has long been clear that modularity is the key to productive and successful programming on any platform. The problem for Java developers is that modular programming entails more than just decomposing a problem into parts; it also involves being able to glue small scale solutions together into an effective whole. Since this type of development is inherent to the functional programming paradigm, it seems natural to use functional programming techniques when developing modular code on the Java platform.

In this article, I've introduced two functional programming techniques that can be easily integrated into your Java development practices. As you've seen here, closures and higher order functions are not completely unfamiliar programming concepts for most Java developers, and they can be effectively combined to create a number of very handy modular solutions.

I hope this article has provided you with a good foundation for incorporating closures and higher order functions into your Java code, as well as giving you a glimpse of the beauty and effectiveness of functional programming. See Resources to learn more about the concepts and technologies discussed in this article.


Resources

About the author

Abhijit Belapurkar has a bachelor of technology degree in computer science from the Indian Institute of Technology (IIT), Delhi, India. He has worked in the areas of architectures and information security for distributed applications for more than 10 years and has used the Java platform to build n-tier applications for more than five years. He presently works as a senior technical architect in the J2EE space, with Infosys Technologies Limited, Bangalore, India.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=10963
ArticleTitle=Functional programming in the Java language
publish-date=07132004
author1-email=abhijit_belapurkar@infosys.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers