I’ve been using Java 8 in some anger lately, and have encountered one or two tips that are worth sharing. I’ll start with some notes about lambda expressions and the Stream API that helped me thinking about how to use functional composition and higher order functions in Java 8, as well as improving code readability:

Background

I had three maps, each with a different but related value class, and all with a String key class. I needed to convert the entries in these maps:

Map<String, Foo> fooMap;
Map<String, Bar> barMap;
Map<String, Baz> bazMap;

into a particular target class and store the results in a list:

List<Target> output;

To perform the conversion I streamed the entry set of each map and used the map function to transform the entry to an instance of the Target class:

fooMap.entrySet().stream().map(entry -> /* return instance of Target */ );
barMap.entrySet().stream().map(entry -> /* return instance of Target */ );
bazMap.entrySet().stream().map(entry -> /* return instance of Target */ );

Good so far. But then the logic in the conversions started building up resulting in long inline definitions. This made the code hard to follow and unpleasant to look at. I wanted to restrict the definitions to the private scope of the executing class so extracted the lambda expressions out to private member variables of the class. This broke the code up a bit but now these variables had ugly type signatures.

Problem 1: Type aliasing to hide ugly type signatures (sort of)

The arrow syntax used for lambda expressions in Java is syntactic sugar for declaring anonymous implementations of the Function interface, which uses generics to specify the type of its input and output, so the signature of a lambda function is Function<Input, Output>. If either Input or Output are also generic, you quickly arrive at the chevron’th circle of hell, as demonstrated by the signatures of the extracted lambda expressions:

Function<Map.Entry<String, Foo>, Target> fooConverter = entry -> ...
Function<Map.Entry<String, Bar>, Target> barConverter = entry -> ...
Function<Map.Entry<String, Baz>, Target> bazConverter = entry -> ...

Solution

Type aliasing is a feature of some other languages that allows you to assign an existing type to another name, as well as the ability to encode type information for convenience as well as to indicate what that type might be used for. For example, in Scala I could write:

type Converter[T] = ((String, T)) => String

and then express the implementations in terms of Converter (assuming immutable.Map and a desire to be explicit):

val fooConverter: Converter[Foo] = { case (k, v) => ... }
val barConverter: Converter[Bar] = { case (k, v) => ... }
val bazConverter: Converter[Baz] = { case (k, v) => ... }

It isn’t possible to create true type aliases in Java, but it is possible to create empty interfaces and use generics to almost achieve the same effect:

public interface Converter<T> extends Function<Map.Entry<String, T>, Target> {}

The function definitions then become:

Converter<Foo> fooConverter = entry -> ...
Converter<Bar> barConverter = entry -> ...
Converter<Baz> bazConverter = entry -> ...

Problem 2: Combining multiple streams

Now I had the contents of the three maps converted to the Target class, I needed to combine them into a single stream and then dump this out to a list. The Stream documentation revealed a useful looking static method called concat which can be used to combine two streams. Initially I used this to combine the three streams but what if I had more than three streams? What if I had more than three hundred? Ideally it won’t matter whether there any amount of streams, including no streams at all.

Stream.concat(
          fooMap.entrySet().stream().map(fooConverter),
          Stream.concat(
                    barMap.entrySet().stream().map(barConverter),
                    bazMap.entrySet().stream().map(bazConverter)));

Solution

There is a functional solution for this too. Stream has a static method, of, which is generic and takes a variable number of elements. This looks promising, but if I pass in my streams, I’ll end up with a stream of streams of Target objects rather than just a stream of Target objects. The reduce method comes to the rescue and allows us to use the concat function to flatten the stream of streams to an Option over a Stream. If there are no elements at all, I get Optional.empty, so adding an orElseGet clause on the end causes either the populated Stream, or the empty stream to be returned:

Stream.of(
          fooMap.entrySet().stream().map(fooConverter),
          barMap.entrySet().stream().map(barConverter),
          bazMap.entrySet().stream().map(bazConverter)
)
.reduce(Stream::concat)
.orElseGet(Stream::empty);

This can then be converted to a List using the toList Collector.