Java 8 Method References

The following is a fairly reasonable block of code that one might find in a project using Guava.

ImmutableList.copyOf(Iterables.limit(Iterables.filter(Iterables.transform(theIterable, new Function<Object, Object>() {
            @Override
            public Object apply(Object input) {
                return changeThing(input);
            }
        }), new Predicate<Object>() {
            @Override
            public boolean apply(Object input) {
                return testThing(input);
            }
        }), 5));

It's fairly hard to tell just from glancing at this code what it is actually doing. First it's transforming a group of Objects into different Objects, then it's filtering them based on the result of some predicate and then taking the first 5 results. Finally it forces the Iterables.doSomething()s to be evaluated by copying the result into an ImmutableList.

The main problem for this code's readability is those pesky anonymous classes for the Predicate and Function. The problem could be ameliorated by extracting them into constants or, if that's not possible static factory methods. However that is just moving the problem elsewhere. There is one line of code in each of those anonymous classes that we actually care about.

Lambdas and Stream can help things...

ImmutableList<Object> result = theIterable.stream()
                .map(s -> changeThing(s))
                .filter(s -> testThing(s))
                .limit(5)
                .collect(ImmutableCollectors.toList());

So that's turned 10 lines of code into 5. However s -> changeThing(s) this still feels like it could be less verbose, doesn't it? Stream.map() and filter() expects a Function and a Predicate to call respectively, which are both @FunctionalInterfaces and expose a single method. In which case, why are we telling Java how to use something if there is only one way to use it?

Method References

ImmutableList<Object> result = theIterable.stream()
                .map(this::changeThing)
                .filter(this::testThing)
                .limit(5)
                .collect(ImmutableCollectors.toList());

Rather than telling the Stream how to use the method, we simply say there exists a method in this class called changeThing and testThing, go use them.

Some more examples...

Iterables.transform(theIterable, this::changeThing);
Iterables.filter(theIterable, MyOtherClass::staticMethod);
Iterables.transform(theIterable, MyOtherClass::new);
Iterables.transform(theIterable, thingInstance::instanceMethod);
Multimaps.index(theIterable, keyMapper::getThing);

This is useful because it means we don't need to create an anonymous class just to invoke an existing method, we can simply reference it to use it as a lambda expression. In cases where you are using an existing project with Guava that has been upgraded to use Java 8 and you don't want to mix Iterables and Stream this lets you take advantage of method references to make the code a little less verbose.

Originally posted on MetaBroadcast's blog.