A lovely look at lambdas

Community post: Functions are Objects; the other point of view

EditorialTeam
duke

Lambdas have been a long-awaited feature in Java- Samir Talwar takes us through their implementation in Java 8.

JAXenter’s latest community blog pick comes
courtesy of Samir
Talwar
, a UK based developer, blogger, and engineer at
Palantir. The original post can be found on his blog
Monospaced Monologues
.

There’s a feature in Java 8 which nicely embodies one of the
differences between the class-oriented structure of Java and
functional languages. It lies in the way you implement lambdas.

You might have seen the syntax already. It
looks something like this:

List<Integer> numbers = ImmutableList.of(2, 9, 8, 3);
List<Integer> squares = numbers.stream()
                           .map(x -> x * x)
                           .collect(toList());
assertThat(squares, contains(4, 81, 64, 9));

It’s quite pretty, and means there’s a separation between
the lazy functional stuff and the normal collections interfaces.
But have you wondered at all what the signature of
Stream::map
is?

Turns out it takes an object, like every other method in
Java.

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

That
Function
 is
java.util.function.Function
, an interface with one abstract
method,
R apply(T t)
. In this case,x
-> x * x
is an implementation of that
Function
interface. But it doesn’t have to be.

Let’s look at another example:

List<Integer> numbers = ImmutableList.of(2, 9, 8, 3);
List<Integer> odds = numbers.stream()
                         .filter(x -> x % 2 == 1)
                         .collect(toList());
assertThat(odds, contains(9, 3));

The signature of
filter
is as follows:

Stream<T> filter(Predicate<? super T> predicate);

This time, my lambda was converted into an implementation of

Predicate
. Same syntax, same number of parameters, but it
has a different type. How did the compiler know how to do that? C#
uses one type for both. Functions are of type
Func<T, U>
, and predicates are simply
Func<T, bool>
. It turns out the Java 8 lambda folk
decided that their implementation of a function object shouldn’t be
special. After all, plenty have existed before in libraries such as
Guava and
Functional Java. So they
came up with a heuristic instead: any interface or abstract class
with a single abstract method is considered a functional interface
and can be implicitly implemented by a lambda expression. This
means that lambdas really are objects like (almost) everything else
in Java. Functions aren’t special; classes and objects are the
building blocks of the programming language, and the functional
aspects of it are designed with that in mind.

Naming

Naming
is important. There’s a new interface in Java 8 called
java.util.function.Supplier
, which has one
method:

T get();
That’s
it. You implement it like this:
final String words = "lots of words";
Supplier<String> lastWord = () -> words.substring(words.lastIndexOf(' ') + 1);

You can make it more complicated,
returning something else if the string is empty or only has one
word, for example. This serves my point well though. It’s obvious
that I can use this to pass around any function that takes no
arguments but returns something. By that logic, a
Future
is a
Supplier
—it takes nothing and returns a value when you call
the
get
method. Why do I need a
Future
interface? (Let’s ignore its other methods for
now.)

It’s because they’re conceptually different.
Futures are not necessarily suppliers, and you might want to treat
them differently. Even if you don’t, giving different names to
separate concepts generally helps you keep your code base
sane.

Here’s another one:

interface Builder<T> {
    T build();
}

Not a supplier. I’m going to have lots of
methods on my builder, of which build is just one, and I want it to
be called exactly that. Not
get
,
build
, because it explains nicely what’s
happening.

And if I want to convert, well, that’s
easy:

gimmeASupplier(() -> builder.build());

And
thanks to method references, we can even short-circuit
that:

gimmeASupplier(builder::build); 

(Ask me about method references some
time.)

And as a bonus

Funnily
enough, Guava’s
Function
type is a functional interface too.
public interface Function<F, T> {
    @Nullable T apply(@Nullable F input);
} 

So if I’m already using Guava and want to
keep at it, there’s no problem. This works just
fine:
 

Iterables.transform(numbers, x -> x * x);

Backwards-compatibility is a lovely thing
when it’s done right. Lambdas in Java have been a long time coming,
but now they’re just around the corner and I’m really looking
forward to them.

If you
want to experiment with lambdas, download the early access release. The latest
versions of IntelliJ
IDEA
have great support for Java 8 with
lambdas.


 


Author
Comments
comments powered by Disqus