Click here to Skip to main content
6,628,613 members and growing! (14,334 online)
Email Password   helpLost your password?
Languages » Java » General     Intermediate License: The Code Project Open License (CPOL)

Functional Java

By Stephen Swensen

Functional programming with functors and object streams in Java
Java, Dev
Version:2 (See All)
Posted:13 Nov 2009
Updated:15 Nov 2009
Views:1,385
Bookmarked:2 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
2 votes for this article.
Popularity: 1.35 Rating: 4.50 out of 5

1

2

3
1 vote, 50.0%
4
1 vote, 50.0%
5

Introduction

The goal of the swensen.functional package is to provide Java with:

  1. a set of generic functors, and
  2. a wrapper type unifying Java arrays and Iterables with many hall-mark functional features.

The project was conceived by the author as a means to grow comfortable with Java, and inspired by the author's experience with C# and F#. The types in swensen.functional do not seek to extend Java as a language, they work with basic language features, making heavy use of generics and anonymous inner classes in particular.

Generic Functors (Func0, Func1, Func2, ... ; Action0, Action1, Action2, ...)

Comparator and Runnable are well-known examples of functors in Java. They are interfaces specifying a single method, and they are often created via anonymous inner classes. With generics, we can forego ad-hoc functors and refine ourselves to two varieties: FuncX (non-Void return type), and ActionX (Void return type), where X is the number arguments encapsulated by our functors (arbitrarily implemented through 5 in swensen.functional). For example, let's take Func2:

package swensen.functional;

/**
 * @author Stephen Swensen
 * A generic functor for non-Void methods with two parameters.
 * @param <T1>	the type of the first parameter of call
 * @param <T2>	the type of the second parameter of call
 * @param <R>	the return type of call
 */
public interface Func2 <T1,T2,R> {
	/**
	 * Invoke this functor synchronously.
	 * @param t1	the first parameter
	 * @param t2	the second parameter
	 * @return	the return value
	 */
	public R call(T1 t1, T2 t2);
}

Here we see that T1, T2, and R are type parameters allowing us to encapsulate any method with two (non-primitive) arguments T1, T2 and a (non-primitive) return type R. We might use it as follows:

Func2<String,String,Integer> func = new Func2<String,String,Integer>() {
	public Integer call(String t1, String t2) {
		t1 = (t1 == null) ? "" : t1.trim();
		t2 = (t2 == null) ? "" : t2.trim();
		return t1.length() - t2.length();
	}
};
		
Integer result = func.call("hello world   ", "  hello world"); //result == 0

swensen.functional also supplies a set of functors designed for compatibility between generic and legacy functors: CallableFunc, ComparatorFunc, and RunnableAction. Each of these abstract classes implements both their indicated legacy functors and their generic analogs, and may be instantiated directly by anonymous inner class or by the static method from overloaded to accept pre-existing instances of either supertype. One other functor, Predicate<T>, is an abstract class implementing Func1<T,Boolean> and exposing several methods for building compound Predicates.

Sequences (Seq)

Seq is a quasi immutable type implementing Iterable and featuring method chaining and lazy evaluation. Seqs are constructed via the static method of overloaded for Iterables and all primitive and generic vararg arrays. The wrapped data-structure is not actually copied into the resulting Seq and it is never modified: all operations against Seq's result in new Seq's (however, the data-structure could be externally modified, compromising the immutability of Seq). This is achieved by using anonymously constructed Iterators to produce Seq elements on demand (a technique used throughout the Seq class):

/**
 * Construct a sequence from a generic array.
 * Lazy Iterable wrapper around arr, no new memory is allocated.
 * Outside structural mutation to arr compromises the immutability of this Seq.
 * Note that since we override Seq.of for each and every primitive array type, we will
 * never get surprising behavior like that seen with Arrays.asList.
 * @param <E>	element type
 * @param arr	an array, may be null
 * @return	a new Seq constructed from arr 
 * (or Seq.EMPTY if arr is null or length 0)
 */
public static <E> Seq<E> of(final E...arr)
{
	if(arr == null || arr.length == 0)
		return Seq.getEmpty();
	
	return Seq.of(new Iterable<E>(){
		public Iterator<E> iterator() {
			return new Iterator<E>(){
				int cur = 0;
				
				public boolean hasNext() {
					return cur < arr.length;
				}

				public E next() {
					if(cur < arr.length)
					        return arr[cur++];
					else
					        throw new NoSuchElementException();
				}
				
				public void remove() {
					throw new UnsupportedOperationException();
				}
			};
		}
	});
}

Seq makes extensive use of the functors we introduced earlier. It includes classic functional methods like filter, map, and foldl along with many others useful for manipulating and generating Seq object streams. For example:

/* elements generated on demand, no memory allocated 
   up front except for Seq object itself. */
Seq<Integer> range1 = Seq.range(1, 10); 

/* range1 is neither mutated nor traversed. 
   could also do range1.append(34,36,38,29), leveraging varargs + autoboxing */
Seq<Integer> range2 = range1.append(Seq.of(new int[] {34, 36, 38, 39})); 

Func1<Integer,Integer> minus3 = new Func1<Integer,Integer>(){
	public Integer call(Integer t1) {
		return t1 - 3;
	}
};

/* could alternatively be a Predicate<Integer> */
Func1<Integer,Boolean> isPositiveOdd = new Func1<Integer,Boolean>(){
	public Boolean call(Integer t1) {
		return (t1 % 2) == 1;
	}
};

/* {1,2,3,4,5,6,7,8,9,10,34,36,38,39} -> 
   {-2,-1,0,1,2,3,4,5,6,7,31,33,35,36} -> {1,3,5,7,31,33,35} */
Seq<Integer> result = range2.map(minus3).filter(isPositiveOdd);

Action1<Object> println = new Action1<Object>() {
	public void call(Object obj){
		System.out.println(obj);
	}
};

/* apply the println Action to each element immediately. */
result.iter(println);

Func2<Integer,Integer,Integer> sum = new Func2<Integer,Integer,Integer>(){
	public Integer call(Integer t1,Integer t2) {
		return t1 + t2;
	}
};

/* 115 */
Integer resultSum = result.foldl(0, sum);

Final Remarks

Additional insight into the swensen.functional package may be gained by studying the source code and reviewing the comments. Any suggestions or bug reports would be much appreciated.

History

Version Date Description
1.00 November 11, 2009 First release
1.01 November 14, 2009

Added null argument checks to several methods
Changed Seq.any and Seq.all to accept Func1 instead of Predicate
Overloaded Seq.min/max/sort for Comparator, Func2, and ComparatorFunc
Fixed Seq.isEmpty()

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Stephen Swensen


Member
I am a consulting C# / ASP.NET / Javascript / SQL developer and business analyst by day, mathematician and programming language enthusiast by night. That's not to mention my passion for music, nature, and most of all family and friends.
Location: United States United States

Other popular Java articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 6 of 6 (Total in Forum: 6) (Refresh)FirstPrevNext
GeneralErrors compiling Pinmembermbedward16:40 16 Nov '09  
GeneralRe: Errors compiling [modified] PinmemberStephen Swensen17:58 16 Nov '09  
GeneralRe: Errors compiling Pinmembermbedward18:49 16 Nov '09  
GeneralRe: Errors compiling PinmemberStephen Swensen19:01 16 Nov '09  
GeneralAn interesting and accessible article Pinmembermbedward0:54 16 Nov '09  
GeneralRe: An interesting and accessible article PinmemberStephen Swensen4:50 16 Nov '09  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 15 Nov 2009
Editor: Deeksha Shenoy
Copyright 2009 by Stephen Swensen
Everything else Copyright © CodeProject, 1999-2009
Web22 | Advertise on the Code Project