The goal of the swensen.functional package is to provide Java with:
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.
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.
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);
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.
| Version | Date | Description |
| 1.00 | November 11, 2009 | First release |
| 1.01 | November 14, 2009 |
Added |
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||