Declaration-Site Variance in a Future Java

In our "Cats & Dogs" blog post we wrote about a security issue that raises when using mutable Java collections.

In particular, type narrowing might lead to heap pollution in the way that

List<Dog> contains a Cat...

Meanwhile the status of JEP 300 has been updated on the OpenJDK project site. The addition of declaration-site variance will increase the expressiveness of Java and eliminate the need of unsafe type casts.

Let's take a look again at the Cats & Dogs example:

interface Pet {}
class Dog implements Pet {}
class Cat implements Pet {}

interface List<T> {}

As of Java 8 we need to narrow types:

List<Dog> dogs = ...;
List<Pet> pets = narrow(dogs); // NEED TO NARROW

where narrowing means in this case that a list that contains dogs can be seen as list that contains pets:

// This is unsafe when List is mutable
static <T> List<T> narrow(List<? extends T> list) {  
    return (List<T>) list;
}

The addition of declaration-site variance to the Java language will allow us to express it directly on the type system:

// a List is covariant in its argument type T
interface List<+T> {}

List<Dog> dogs = ...;
List<Pet> pets = dogs // OK!

But soon we will discover problems when our API evolves. Let's add a prepend() method to List. The compiler will not pass:

interface List<+T> {

    // COMPILER ERROR: Covariant type T occurs in contravariant position in type T of value t
    List<T> prepend(T t);

}

Our types need to reflect that we may add elements in a contravariant position to a List:

interface List<+T> {

    <U super T> List<U> prepend(U u);

}

Given that we are allowed to use prepend() as expected:

List<Dog> dogs = ...;
List<Pet> pets = dogs;
pets = pets.prepend(new Cat());

However, Java currently does not support the declaration of lower bounded generics like U super T on the method level.

But I'm sure that problem will be solved with JEP 300!

Have fun!

- Daniel