My ¢2 on Java Extension Methods

Published 05 December 07 05:38 PM | andersnoras 

A while a go Neil Gafter proposed extension methods as a new feature in Java 7. This is an interesting proposal and if it is implemented, it would make Quaere even better. I'll skip the tired list sorting example from the discussions and think out loud about how Quaere would benefit from extension methods.

First lets look at some simple features. Today, you can union two sequences using the union static import:

    List<Integer> numbersA = Arrays.asList(0, 2, 4, 5, 6, 8, 9);
List<Integer> numbersB = Arrays.asList(1, 3, 5, 7, 8);
Iterable<Integer> uniqueNumbers = union(numbersA, numbersB);

If we can introduce the union method as an extension method on the List interface, the example could be written like this:

    List<Integer> numbersA = Arrays.asList(0, 2, 4, 5, 6, 8, 9); 
List<Integer> numbersB = Arrays.asList(1, 3, 5, 7, 8);
Iterable<Integer> uniqueNumbers = numbersA.union(numbersB);

It's not a huge thing, but it makes the queries a little nicer because it now read entirely from left to right. We could also use extensions to make queries a little more compact. Instead of writing:

    List<Integer> numbers = 
Arrays.asList(5, 4, 1, 3, 9, 8, 6, 7, 2, 0);
Iterable<Integer> lowNums =
from("n").in(numbers).
where(lt("n", 5)).
select("n");

...we could do something like this...

    List<Integer> numbers = 
Arrays.asList(5, 4, 1, 3, 9, 8, 6, 7, 2, 0);
Iterable<Integer> lowNums =
numbers.where(lt("n", 5));

However, I think we'd have to keep the more verbose query syntax as it is today, because it is a little cumbersome to write advanced queries without any language support.

The biggest potential lies in how predicates are expressed. Today, we have to write the n < 5 predicate as lt("n",5). Both Thomas Mueller and Faraz Amhed are working on some interesting alternatives that would enable us to write predicates like this in a more straight forward manner, but none of these have made it into Quaere yet. With extension methods we could improve today's implementation by extending java.lang.String. If we did, the predicate could look like this n.lt(5). Extending a common class like String has its issues and .NET developers have experienced this with C# extension methods.

While we're at C#, I'd like to contribute my own suggestion on how extension methods could be implemented in Java. I really like the C# take on this. Below is an example of how we could extend .NET's IList interface with the union method from earlier. This is of course for example only as LINQ already does this.

public static IEnumerable<T> union(this IList<T> sourceA, IList<T> sourceB) {
List<T> result = new List<T>();
result.add(sourceA);
foreach (T b in sourceB) {
if (!result.Contains(b)) {
result.Add(b);
}
}
}

Notice the this keyword on the first argument, this indicates that the C# compiler should treat this method as an extension. Read my Bare Naked LINQ post for more on .NET extension methods.

I'd like to see a similar approach if extensions are added to Java. This preserves a familiar syntax and uses familiar concepts to achieve it. After all, extension methods only need to be compile time magic - there is no need to change the JVM.

Technorati tags: , ,
Filed under: , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Howard Lovatt said on December 6, 2007 5:21 PM:

You could alternatively use a with clause (like Pascal), and give it the synbol -> (or the keyword with), e.g:

Iterable<Integer> uniqueNumbers = numbersA -> union(numbersB);

This could also be applied to instance methods and non statically imported statics as well as statically imported statics and to blocks of commands, e.g.:

list1 -> { addAll(list2); addAll(list3) };

list1 is now the concatonation of the three lists.

As you point out in your post the primary advantage is the left to right reading. I favour the syntax change to -> so that it is obvious what is going on - you are just passing in the first argument.

A language I use a lot, Mathematica, does this (though it uses //). I find it very convenient for DSL type stuff.

# Howard Lovatt said on December 13, 2007 2:28 PM:

I have blogged about an alternative, with clauses:

http://www.artima.com/weblogs/viewpost.jsp?thread=220783

Typical usages are:

list -> synchronizedList() -> sort();

list -> { add( 1, "A" ); add( 2, "B" ); };

Home home = new Builder() -> { setWindows( windows ); setDoors( doors ); makeHome(); };

# Jörn Zaefferer said on December 31, 2007 9:32 AM:

I think Scala's implicit conversion are an interesting solution to this problem, though in Scala I'd prefer a functional approach anyway:

val numbers =

       Array(5, 4, 1, 3, 9, 8, 6, 7, 2, 0)

val lowNums =

           numbers.filter(_ < 5);

Leave a Comment

(required) 
(optional)
(required) 
Enter the code you see below