18 May 2019

Thoughts on Type Variance


in Java arrays are covariant (can be assigned to an array of supertype)

Integer[] arr = {1, 2, 3};
Object[] objArr = arr; // allowed because of being covariant
objArr[0] = "Hello"; // runtime error: ArrayStoreException

But it comes at a cost that, it might fail at runtime (as in the last line)

in Kotlin, arrays are invariant:

val arr = arrayOf(1, 2, 3)
val objArr: Array<Any> = arr // compilation error

So, invariant, doesn't consider List<Integer> to substitue List<Object> nor vice versa,

However, covariant consider List<Integer> to substitue List<Object>, but not vice versa.
And contravriant consider List<Object> to substitude List<Integer>, but not vice versa.


in Kotlin, Lists (immutable lists) are covariant, the following works:
val list = listOf(1, 2, 3)
val objList: List<Any> = list

because Lists are immutable, we can't fail as in the Java array case.

in contravariant, the super type can substitute the subtype (might seems illogical at first),
but look at this (fiction example):

operateOnIntegers(Integer i) {
i.setValue(50)
}

Number n = 30
operateOnIntegers(n)

because operateOnIntegers expected a narrower type than the passed one (expects Integer and passed Number),
it will never write a value into it that exceeds its limits.



No comments: