Subtyping Relationships of Generic Types
Instantiated generic types also have subtyping relationships. For example:
interface I<X, Y> { }
class C<Z> <: I<Z, Z> { }
Based on class C<Z> <: I<Z, Z> { }, we know that C<Bool> <: I<Bool, Bool> and C<D> <: I<D, D> hold, among others. This can be interpreted as “For all types Z without type variables, C<Z> <: I<Z, Z> holds.”
However, for the following code:
open class C { }
class D <: C { }
interface I<X> { }
I<D> <: I<C> does not hold (even though D <: C holds). This is because in the Cangjie language, user-defined type constructors are invariant at their type parameters.
The formal definition of variance is: If A and B are (instantiated) types, and T is a type constructor with a type parameter X (e.g., interface T<X>), then:
- If
T(A) <: T(B)if and only ifA = B, thenTis invariant. - If
T(A) <: T(B)if and only ifA <: B, thenTis covariant atX. - If
T(A) <: T(B)if and only ifB <: A, thenTis contravariant atX.
In the current version of Cangjie, all user-defined generic types are invariant at all their type parameters. Therefore, given interface I<X> and types A, B, I<A> <: I<B> holds only if A = B. Conversely, if I<A> <: I<B> is known, we can deduce A = B (with the exception of built-in types: built-in tuple types are covariant at each of their element types; built-in function types are contravariant at their parameter types and covariant at their return types.)
Note:
For types other than
classthat implement interfaces, the subtyping relationship between the type and the interface cannot serve as a basis for covariance or contravariance.
Invariance limits some expressive power of the language but also avoids certain safety issues, such as the “covariant array runtime exception” problem.