Versions/v1.0.5/English

Subtyping Relationships of Generic Types

Sections1
On This Page1
API Symbols3

Subtyping Relationships of Generic Types

Instantiated generic types also have subtyping relationships. For example:

Based on class C <: I { }, we know that C <: I and C <: I hold, among others. This can be interpreted as "For all types Z without type variables, C <: I holds."

However, for the following code:

I <: I 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), then:

- If T(A) <: T(B) if and only if A = B, then T is invariant.
- If T(A) <: T(B) if and only if A <: B, then T is covariant at X.
- If T(A) <: T(B) if and only if B <: A, then T is contravariant at X.

In the current version of Cangjie, all user-defined generic types are invariant at all their type parameters. Therefore, given interface I and types A, B, I <: I holds only if A = B. Conversely, if I <: I 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 class that 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.