Friday, February 17, 2012

Lambdas and functional interfaces in Java 8

Lambda's in Java 8 are going to be very nice. I like how they integrate with the existing type system. They automatically convert to so called interface types (previously SAM types), which are interfaces where only one method needs to be implemented. That allows to just use e.g. Google Guava right away without any modifications.

Also this means that there are no types like Scala's Function23 and you even can have lambdas with 24 arguments (not saying that this is a particularly good idea :-)).

The most commonly used functional interfaces will likely come with java.util.* since they are needed there. So hopefully we won't be forced to redeclare the obvious functional interfaces all over the place. I consider such a commonly used set of generic interface types important since otherwise we will end up with redefining them in every project.


The downside of not having explicit "function types" is that writing signatures will still be quite clumsy.

To declare a higher-order method like e.g. 'map' on java.lang.Iterable, in Java you would have to invent some functional interface and then write :

public <R> Iterable<R> map(Function<? super T, ? extends R> predicate)

Compared to Xtend, which also supports the conversion to functional interfaces, there is a special type signature for functions, which will get the generics (i.e. upper and lower bounds) straight for you. So in Xtend you can just write the following instead :

def <R> Iterable<R>  map( (T)=>R predicate )

(T)=>R really is just a short-form of Function<? super T, ? extends R>

Using this type signature in casts also allows for easily convert from one functional interface to another. Also you can declare lambdas without any context type information. In Java 8 the following will not be possible, since the context type (java.lang.Object) is not an interface type:

Object myFunction = (String s)-> s.toUpperCase();

So instead you'd write:

Function<? super String,? extends String> myFunction = s -> s.toUpperCase()

In Xtend you can instead just use type inference and write :

val myFunction = [String s| s.toUpperCase()]

So as you can see having a syntactical short form for type signatures like Xtend would improve readability of signatures a lot and would also help developers getting the upper- and lower bounds right. But even without this using auto conversion to interface types is a great way to add lambdas to Java.

3 comments:

  1. Don't you also need the declaration for T in Java, i.e.,

    public Iterable map(Function predicate)

    Java 8 lambda look promising... but once you got used to the clean way of Xtend it will be hard to get used to the verbose style of Java ;)

    ReplyDelete
  2. the editor for comments wiped out the symbols... I meant, you also need to declare T (not only R) as a parametric type for map in Java?

    ReplyDelete
  3. Yes, but the method was meant to be defined as a member of the type Iterable. So T is a parameter to the type rather than to the method.

    ReplyDelete