Whether you’re already using Java 10 or not, I’m sure you’ve heard all about

var
, Java’s new keyword reserved type name that allows you to declare local variables without having to specify their types. While
var
looks simple at first glance, it opens the door to a richer type system and can be turned into a surprisingly powerful tool with which you can do some really cool things. Today I want to look at how to use it to work with intersection types:

// `elements` should implement both
// `Closeable` and `Iterator<String>`
// (DOES NOT COMPILE!)
Closeable & Iterator<String> elements = // ...
// `try` only works because `elements` is `Closeable`
try (elements) {
    // the method `stream` expects an `Iterator<E>`,
    // (and returns a `Stream<E>`), so it can only be
    // called if `elements` implements it
    Optional<String> first = stream(elements)
        .filter(s -> s.startsWith("$"))
        .findAny();
}

The declaration of

elements
looks pretty weird: It wants the variable to be of two types,
Closeable
and
Iterator<String>
. That’s called an intersection type, but Java’s syntax doesn’t allow them and so the code doesn’t even compile – our goal is to find a variant that does. After some background on the why, what, and how of intersection types in Java, I will show you how to use them with
var
.

Spoiler: The declaration will end up as

var elements = // ...
.

Overview

To see the code snippets in action, check my Java 10 demo project.

CodeFX @ YouTube

Need to catch up on the 'var' basics? There's a thorough introduction on my YouTube channel.

Check it out and subscribe!

What Are Intersection Types?

We’ve all been in this situation: You’re writing a method and at some point realize that some parameter’s type isn’t powerful enough – you need more! Maybe a

List
instead of a
Collection
or a
Megacorp
instead of a mere regular
Corporation
. Often that’s pretty straight forward: There’s a type which has what you need, so you can simply use it.

Other times it’s not so easy, though. As an example, say you need an

Iterator
to also be
Closeable
, so you can do this:

static <E> Optional<E> firstMatch(
        /* Closeable & Iterator<E> */ elements,
        Predicate<? super E> condition) {
    try (elements) {
        // `stream` turns `Iterator<E>` into `Stream<E>`
        return stream(elements)
            .filter(condition)
            .findAny();
    }
}

You want to iterate over

elements
, so it needs to be an
Iterator
, but you also want the safety of a
try
-with-resources block, so it needs to be
Closeable
as well. If you think of the type
Iterator
as a circle that contains all variables of that type and likewise for
Closeable
,
elements
needs to be in the intersection of these circles – it needs to be of an intersection type of
Iterator
and
Closeable
.
Intersection types - here of Closeable and Iterator

Just Create A New Interface

The obvious way to limit

elements
to the desired types is to create a new interface:

public interface CloseableIterator<E> extends Closeable, Iterator<E> { }

static <E> Optional<E> firstMatch(
        CloseableIterator<E> elements,
        Predicate<? super E> condition)
        throws IOException {
    // ...
}

That’s great if you have control over all implementations of

Closeable
and
Iterator
and can change them to also implement
CloseableIterator
. But what if you don’t? Neither the JDK nor third-party libraries and frameworks know anything about your new interface and although they might return instances that implement both
Closeable
and
Iterator
, you still couldn’t pass them to
firstMatch
:

// `Scanner` implements `Iterator<String>` and `Closeable`
Scanner scanner = new Scanner(System.in);
Optional<String> dollarWord =
    // compile error because `scanner` is no `CloseableIterator` :(
    firstMatch(iterator, s -> s.startsWith("$"));

No, what you really need is a way to express

Closeable & Iterator<E>
without creating a new interface.

Declaring Intersection Types With Generics

Generics allow intersection types in bounded type parameters

While you can’t declare a variable as

Closeable & Iterator<E>
, that expression can be syntactically valid in Java. Just not as a type in a variable declaration but as a bounded type parameter, for example in a generic method:

private static <E, T extends Closeable & Iterator<E>>
        Optional<E> firstMatch(T elements, Predicate<? super E> condition)
        throws IOException {
    // ...
}

That’s not straight forward, but it works: First you declare a new type parameter

T
which has to extend
Closeable
and
Iterator
– here the ampersand is syntactically correct and limits
T
to the intersection of these two types – and then you declare
elements
as being of that type.

The next step is to get an actual instance of

Closeable & Iterator
. That’s pretty easy, every class that implements both interfaces, for example
Scanner
, works fine:

Scanner scanner = new Scanner(System.in);
Optional<String> dollarWord = firstMatch(
    scanner,
    s -> s.startsWith("$"));

But what if the instance is not so easy to create? What if you need to involve another method and its return type can not be specific, like

Scanner
but needs to be the general intersection? We can once again use generics for that:

@SuppressWarnings("unchecked")
private static <T extends Closeable & Iterator<String>>
        T createCloseableIterator(boolean empty) {
    if (empty)
        return (T) new Empty();
    else
        return (T) new Scanner(System.in);
}

You can combine

createCloseableIterator
and
firstMatch
as follows:

Optional<String> dollarWord = firstMatch(
    createCloseableIterator(empty),
    s -> s.startsWith("$"));

That’s actually pretty neat and all of that works without

var
, so where does local-variable type inference enter the picture? That’s next!

Declaring Variables Of Intersection Types

The construction we have so far has the weak spot that it breaks down under refactoring. Want to extract a variable for

createCloseableIterator(empty)
? That’s too bad because, without
var
, you can’t:

// does not compile
Closeable & Iterator<String> elements = createCloseableIterator(empty);
// compiles, but can not be passed to `firstMatch`
Closeable elements = createCloseableIterator(empty);
Iterator<String> elements = createCloseableIterator(empty);
// compiles and can be passed, but can fail at run time
// (depending on `empty`)
Scanner elements = (Scanner) createCloseableIterator(empty);
Empty elements = (Empty) createCloseableIterator(empty);

What you really need is the first line, but Java’s syntax won’t let you compile it. That doesn’t mean the JVM can’t handle it, though. In fact, this would work:

static <T extends Closeable & Iterator<String>>
        void readAndPrint(boolean empty)
        throws IOException {
    T elements = createCloseableIterator(empty);
    Optional<String> dollarWord =
        firstMatch(elements, s -> s.startsWith("$"));
    System.out.println(dollarWord);
}

This goes too far, though. We’re now exposing a type

T
to callers of this method that they see neither as parameter nor return type and would clearly be confused by. And imagine what the method would look like if we needed another such variable!

No,

createCloseableIterator
and
firstMatch
have a right to a complicated signature because they actually need it, but
readAndPrint
is ridiculous! Is there no better way to declare
elements
?

After all this build-up, and given your knowledge of how

var
works, the solution is rather straight forward:

static void readAndPrint(boolean empty) throws IOException {
    var elements = createCloseableIterator(empty);
    Optional<String> dollarWord =
        firstMatch(elements, s -> s.startsWith("$"));
    System.out.println(dollarWord);
}

Use var to capture a generic method’s returned intersection type

There you go, with

var
you can declare
elements
in a way that lets the compiler deduce that its type is the intersection of
Closeable
and
Iterator
, just like you wanted. That means you can now freely declare such variables and use them across methods without having to expose your callers to crazy generic signatures.

Reflection

While the concept of intersection types is quite simple and a common theme in other (JVM) languages like Scala, Java’s lack of first-class support makes it complicated to work with. You have to resort to non-trivial constructions using generics and that always reduces the code’s readability and accessibility. Still, there are situations where they are the best available solution and their added complexity is acceptable.

With the introduction of

var
the situation improved considerably. Even if it’s still a long shot from proper support, being able to easily declare variables of an intersection type makes them much more usable. If you haven’t already, it is high time to add intersection types to your tool belt.

The post Unlocking Intersection Types With ‘var’ In Java 10 appeared first on blog@CodeFX.