benjiPosted under Java.

Java allows casting to an intersection of types, e.g. (Number & Comparable)5. When combined with default methods on interfaces, it provides a way to combine behaviour from multiple types into a single type, without a named class or interface to combine them.

Let’s say we have two interfaces that provide behaviour for Quack and Waddle.

interface Quacks {
    default void quack() {
        System.out.println("Quack");
    }
}
interface Waddles {
    default void waddle() {
        System.out.println("Waddle");
    }
}

If we wanted something that did both we’d normally declare a Duck type that combines them, something like

interface Duck extends Quacks, Waddles {}

However, casting to an intersection of types we can do something like

with((Anon & Quacks & Waddles)i->i, ducklike -> {
    ducklike.quack();
    ducklike.waddle();
});

What’s going on here? Anon is a functional interface compatible with the identity lambda, so we can safely cast the identity lambda i->i to Anon.

interface Anon {
    Object f(Object o);
}

Since Quacks and Waddles are both interfaces with no abstract methods, we can also cast to those and there’s still only a single abstract method, which is compatible with our lambda expression. So the cast to (Anon & Quacks & Waddles) creates an anonymous type that can both quack() and waddle().

The with() method is just a helper that also accepts a consumer of our anonymous type and makes it possible to use it.

static <T extends Anon> void with(T t, Consumer<T> consumer) {
    consumer.accept(t);
}

This also works when calling a method that accepts an intersection type. We might have a method that accepts anything that quacks and waddles.

public static <Ducklike extends Quacks & Waddles> void doDucklikeThings(Ducklike ducklike) {
    ducklike.quack();
    ducklike.waddle();
}

We can now invoke the above method with an intersection cast

doDucklikeThings((Anon & Quacks & Waddles)i->i);

Source code

You can find complete examples on github

Enhancing existing objects

Some have asked whether one can use this to add functionality to existing objects. This trick only works with lambdas, but we can make use of a delegating lambda, for common types like List that we might want to enhance.

Let’s say we are fed up of having to call list.stream().map(f).collect(toList()) and wish List had a map() method on it directly.

We could do the following

List<String> stringList = asList("alpha","bravo");
with((ForwardingList<String> & Mappable<String>)() -> stringList, list -> {
    List<String> strings = list.map(String::toUpperCase);
    strings.forEach(System.out::println);
});

Where Mappable declares our new method

interface Mappable<T> extends DelegatesTo<List<T>> {
    default <R> List<R> map(Function<T,R> mapper) {
        return delegate().stream().map(mapper).collect(Collectors.toList());
    }
}

And DelegatesTo is a common ancestor with our ForwardingList

interface DelegatesTo<T> {
    T delegate();
}
interface ForwardingList<T> extends DelegatesTo<List<T>>, List<T> {
    default int size() {
        return delegate().size();
    }
    //... and so on
}

Here’s a full example

2 Responses to “Anonymous Types in Java”

  1. Sebastian

    Thanks for the entertaining post. I have two comments and a question.
    1. The type parameter of the with() method does not really need to extend Anon.
    2. I’d prefer the signature of Anon#f() to be void f(), and notate the lambda as () -> {}

    My question: Here’s a factory method for ducklike things:

    @SuppressWarnings(“unchecked”)
    static <Ducklike extends Quacks & Waddles> Ducklike ducklikeThing() {
    return (Ducklike)(Anon & Quacks & Waddles)() -> {};
    }

    Why does calling doDucklikeThings(ducklikeThing()) compile, but calling with(ducklikeThing(), …) does not ?

  2. benji

    Hi Sebastian,

    I agree the supplier form of the lambda looks a little less terrible. Anon is only really used to encourage the passing of a lambda.

    Your example doesn’t compile when used with the with method because you don’t have Anon in the return type

    static <Ducklike extends Anon & Quacks & Waddles > Ducklike ducklikeThing() would work

Leave a Reply

  • (will not be published)