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