Covariance, invariance, contravariance overview of collections in Java 11, vavr, guava.
Covariance, invariance, contravariance overview of collections in Java 11, vavr, guava.
Reference: https://dzone.com/articles/covariance-and-contravariance
Reference: https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance
Reference: https://medium.freecodecamp.org/understanding-java-generic-types-covariance-and-contravariance-88f4c19763d2
If A is a subtype of B then X[A] should be a subtype
of X[B].
If A is a supertype of B then X[A] should be a
supertype of X[B].
Variance refers to how subtyping between more complex
types relates to subtyping between their components.
Generics in Java are invariant. (Java has no way
of knowing at runtime the type information of the
type parameters, due to type erasure)
Arrays in java are covariant.
No easy examples in java (unless wildcards).
Generics are invarant
With wildcards, it’s possible for generics to support
covariance and contravariance.
? extends Integer
List<Integer> ints = new LinkedList<>();
List<? extends Number> nums = ints;
? super Integer
List<Integer> ints = new LinkedList<>();
List<? super Integer> nums = ints;
Covariance could be dangerous (JavaUtilCollectionsTest
):
Dog[] dogs = new Dog[5];
Animal[] animals = dogs;
animals[0] = new Cat(); // ArrayStoreException
Mutability combined with covariance would break type safety:
List<String> strs = new ArrayList<String>();
List<Object> objs = strs; // !!! The cause of the upcoming problem sits here. Java prohibits this!
objs.add(1); // Here we put an Integer into a list of Strings
String s = strs.get(0); // !!! ClassCastException: Cannot cast Integer to String
Note that covariance can’t be dangerous with
immutable / readonly collections, cause we can’t
modify them.
static void process(List<Animal> animals) {
...
}
process(new LinkedList<Dog>()) // compile time error
process(Collections.unmodifiableList(new LinkedList<Dog>()))
we have hierarchical structure:
interface Animal extends Comparable<Animal>
class Cat implements Animal
class Dog implements Animal
class BigDog extends Dog
what we can’t do?
List<Dog> dogs = new LinkedList<>();
List<Animal> animals = dogs; // compile time error
what we can do?
using vavr:
HashSet<Dog> dogs = HashSet.of(new Dog(), new Dog());
HashSet<Animal> animals = HashSet.ofAll(dogs);
HashSet<Animal> animals2 = HashSet.narrow(dogs);
HashSet<Animal> animals3 = HashSet.of(new Dog(), new Dog());
HashSet<T> ofAll(java.lang.Iterable<? extends T> elements)
HashSet<T> narrow(io.vavr.collection.HashSet<? extends T> hashSet)
VavrCollectionsTest
tests for: HashSet
, List
,TreeSet
, HashMap
ImmutableSet<Dog> dogs = ImmutableSet.of(new Dog(), new Dog());
ImmutableSet<Animal> animals = ImmutableSet.of(new Dog(), new Dog());
ImmutableSet<Animal> animals2 = ImmutableSet.copyOf(Collections.<Dog>emptySet());
ImmutableSet<E> copyOf(Collection<? extends E> elements)
GuavaCollectionsTest
tests for: ImmutableSet
,ImmutableList
, ImmutableSortedSet
, ImmutableMap
pure java
List<Dog> dogs = List.of(new Dog(), new Dog());
List<Animal> animals = List.of(new Dog(), new Dog());
List<Animal> animals2 = List.copyOf(dogs);
Collections.unmodifiableXXX
List<Dog> dogs = new LinkedList<>();
dogs.add(new Dog());
List<Animal> dogsAsAnimals = Collections.unmodifiableList(dogs);
JavaUtilCollectionsTest
tests for:List
, Collections.unmodifiableList
,Set
, Collections.unmodifiableSet
,Map
, Collections.unmodifiableMap
,Collections.unmodifiableNavigableSet
,Collections.unmodifiableSortedSet
,Collections.unmodifiableCollection
note that you could also use wildcards:
covariance, read-only
Dog dog = new Dog();
List<Dog> dogs = new LinkedList<>();
dogs.add(dog);
List<? extends Animal> dogsAsAnimal = dogs;
// dogsAsAnimal.add(new Dog()); compile time error
// Dog getDog = dogsAsAnimal.get(0);
Animal getDog = dogsAsAnimal.get(0);
assertThat(getDog, is(dog));
contravariance, write-only
List<Dog> dogs = new LinkedList<>();
List<? super Dog> onlyDogSpecies = dogs;
onlyDogSpecies.add(new Dog());
onlyDogSpecies.add(new BigDog());
// Animal getDog = dogsAsAnimal.get(0); compile time error
assertThat(onlyDogSpecies.size(), is(2));