Saturday, November 12, 2011

Lambdas in Java: Playing with the snapshot binary

The first binary snapshots of the Project Lambda repository were made available yesterday. I hadn't bothered to check out and build the code before now, but with only having to download the snapshot I decided to give it a try. I'd been following the mailing list and looking at the code changes to the JDK libraries, so I more or less knew what to expect.

In general, things worked nicely. While I wasn't a big fan of it at first, I do like how concise the new syntax is. It's similar to the C# syntax:

people.filter(p -> p.getAge() < 50)
    .map(p -> p.getFirstName() + " " + p.getLastName())
    .forEach(n -> { System.out.println(n); });

It's pretty much what you'd expect, and obviously nothing revolutionary... just long overdue in my opinion. What I'm really excited about is the improvements to APIs (and new APIs) that concise lambda expressions will allow in Java. I experimented with just a few examples, including:

  • A table model that uses a map function to derive column values from objects.
  • A logging API that can take a Supplier for the message so that it only builds the message if needed.
  • A version of Guava's Optional that has a map function. Far too clunky to be very useful with anonymous inner classes, very nice with a concise lambda expression:
  • A framework for async tasks with callbacks based on Guava's ListenableFuture code that I've been playing with. It's so much nicer when there's so little syntactic overhead for both submitting tasks (since lambdas can be used for Runnable and Callable) and adding callbacks (since they can be SAM types too).

Extension methods should also be a huge benefit. While it could be considered unfortunate that they don't allow you to add methods to others' APIs, I think that's a reasonable decision for Java. Giving API authors the ability to extend interfaces will allow APIs to evolve over time in ways they couldn't before.

There were a couple issues I noticed, but they weren't a big deal and are to be expected with something that's very much in progress.

Extension methods only work for JDK classes out of the box because currently a bytecode weaving tool is used to simulate VM support for extension methods (which isn't there yet). That tool was run as part of the snapshot build, but has to be run separately on any other classes that you want to support extension methods. For example, an AbstractMethodError is thrown if you try to call filter on Guava's ImmutableList... the compiler knows that you should be able to call it since filter is defined on Iterable, but without the VM support or having run the weaver for ImmutableList, it fails at runtime. (Mike Duigou explains how to use the weaver here.)

Support for method references seems kind of shaky. The compiler threw an exception in one situation where I tried to use a method reference and I got compile errors in a number of other situations where I thought a method reference should work. In some other situations, they worked. A final syntax hasn't been decided on for method references, so I imagine support for them just isn't as far along.

There were some unfortunate overload resolution shortcomings. For example, neither of the following statements compile:

Comparator<String> c = Comparators.comparing(s -> s.length());
Comparator<String> c = Comparators.comparing(
    (String s) -> s.length());

You have to write one of the following, so it know which kind of Mapper you want the lambda to be:

Comparator<String> c = Comparators.<String, Integer>comparing(
    s -> s.length()); // Mapper<String, Integer>
Comparator<String> c = Comparators.comparing(
    (IntMapper<String>) s -> s.length()); // IntMapper<String>

I hope it will be possible to change the resolution to choose the most specific interface (in this case IntMapper<String>) so I don't have to specify it unless I want to.

Anyway, it was fun to be able to actually write and run code using lambda expressions in Java. Between Project Lambda and JSR-310 (the new date/time API), I'm really looking forward to JDK 8... too bad it isn't coming until 2013!