Union types in Java

Michal Pawluk picture Michal Pawluk · Mar 26, 2018 · Viewed 14k times · Source

I've been working with C# for a while and trying to get more familiar with Java. So I'm trying to migrate some of the basic patterns I use on daily basis in C# even only to understand the gap between JVM and dotnet and figure out how to deal with them. Here is the first problem I encountered - an option type - somethiong which is quite easy to achieve in many languages i.e. Koltlin:

sealed class Option<out T : Any> {
    object None : Option<Nothing>()
    data class Some<out T : Any>(val value: T) : Option<T>()}

so I can easily create a map functor:

fun <T : Any, B : Any> Option<T>.map(f: (T) -> B): Option<B> =
    when (this) {
        is Option.None -> Option.None
        is Option.Some -> Option.Some(f(this.value))} 

Is this something I can achieve in Java? Im not concerned about the lack of extentions methods, I can leave without that, but how to perform the actual type matching without having to rely on an unchecked cast? At least thats what IntelliJ is complaining about...

Answer

Eric picture Eric · Jun 29, 2018

in the specific case you mentioned, the following would work:

Java doesn't have pattern matching. The closest you can get to pattern matching in Java is with the visitor pattern.

usage:

UnionType unionType = new TypeA();

Integer count = unionType.when(new UnionType.Cases<Integer>() {
    @Override
    public Integer is(TypeA typeA) {
        // TypeA-specific handling code
    }

    @Override
    public Integer is(TypeB typeB) {
        // TypeB-specific handling code
    }
});

boilerplate code:

interface UnionType {
    <R> R when(Cases<R> c);

    interface Cases<R> {
        R is(TypeA typeA);

        R is(TypeB typeB);
    }
}

class TypeA implements UnionType {

    // ... TypeA-specific code ...

    @Override
    public <R> R when(Cases<R> cases) {
        return cases.is(this);
    }
}

class TypeB implements UnionType {

    // ... TypeB-specific code ...

    @Override
    public <R> R when(Cases<R> cases) {
        return cases.is(this);
    }
}