Interface-only programming in Java
Tags: java, lang, beginners, tutorial
Published: 2019-11-05 06:32:44

---
title: Interface-only programming in Java
published: true
description: Funny yet useful trick with Java interfaces and monads
tags: #java #lang #beginners #tutorial
---

Combination of `default` methods in Java interface together with [Monad](https://dev.to/siy/monads-for-java-programmers-in-simple-terms-1959) pattern enables writing 'interface only' classes. There classes don't have separate implementation classes, just interfaces. Well, of course, technically they have implementation, but that implementation is an inline tiny anonymous class.

I'll demonstrate the idea by writing (incomplete) implementation of `Maybe` monad, sometimes called `Optional` or `Option`. This container is used to represent values which might be present or missing. For example `Map.get(key)` can use this container to represent result of the lookup. If value is found then `Some` value is returned, otherwise `None` (empty container) is returned.

Java 8 has `Optional` which implements such a behavior, so we can look at it's API and implement something similar:

```java
public interface Option<T> {
    <U> Option<U> map(final Function<T, U> mapper);
    
    <U> Option<U> flatMap(final Function<T, Option<U>> mapper);
    
    Option<T> ifPresent(final Consumer<T> consumer);
    
    Option<T> ifEmpty(final Runnable consumer);

    T otherwise(final T replacement);
}
```
If we try to implement this interface straightforward, we should create a class which will hold value. Then in every method we'll check if that value is `null` and if yes, do one action and if not - do other action. Looks like code smell - repeating logic. Let's extract this logic into method, which will accept two functions and will call them depending on the `Option` state (i.e. value is present or not):

```java
   <U> U map(final Function<T, U> presentMapper, final Supplier<U> emptyMapper);

```
Now we can represent all remaining methods using this new `map()` method:
```java
    default <U> Option<U> map(final Function<T, U> mapper) {
        return Option.option(map(mapper, () -> null));
    }

    default <U> Option<U> flatMap(final Function<T, Option<U>> mapper) {
        return map(mapper, Option::empty);
    }

    default Option<T> ifPresent(final Consumer<T> consumer) {
        map(v -> {consumer.accept(v); return null;}, () -> null);
        return this;
    }

    default Option<T> ifEmpty(final Runnable consumer) {
        map(v -> v, () -> { consumer.run(); return null;} );
        return this;
    }

    default T otherwise(final T replacement) {
        return map(v -> v, () -> replacement);
    }

```
Now all we need is two static factory methods - one for creating empty container and one for container with value. Since for all functionality we need only one method, we can use anonymous classes:
```java
    static <T> Option<T> option(final T value) {
        return (value == null) ? empty() : new Option<T>() {
            @Override
            public <U> U map(final Function<T, U> presentMapper, final Supplier<U> emptyMapper) {
                return presentMapper.apply(value);
            }
        };
    }

    static <T> Option<T> empty() {
        return new Option<T>() {
            @Override
            public <U> U map(final Function<T, U> presentMapper, final Supplier<U> emptyMapper) {
                return emptyMapper.get();
            }
        };
    }

```
That's it, all necessary functionality is implemented inside interface.

Interestingly enough is that there is only one conditional operator in the whole code. It is necessary just because we followed Java convention to recognize `null` as missing value. This is not strictly necessary and this implementation may hold `null` values and still will be able to distinguish present and missing values.
Some other interesting observations:
 - Empty instance actually holds no value, there is no even field for it (unlike Java 8 `Optional`). We can optimize implementation and return same instance for empty container every time.
 - There is no explicit instance variable for stored value, it is implicitly stored by Java compiler while creating non-empty instance.
 - Lack of branching inside code should help achieve better performance at run time. In this case gain will be negligible, but the technique is general and might be used in cases where gain might be significant.