实例化后的泛型类型间也有子类型关系。例如当我们写出下列代码时,
interface I<X, Y> { }
class C<Z> <: I<Z, Z> { }
根据第 3 行,便知 C<Bool> <: I<Bool, Bool> 以及 C<D> <: I<D, D> 等。这里的第 3 行可以解读为“于所有的(不含类型变元的) Z 类型,都有 C<Z> <: I<Z, Z> 成立”。
但是对于下列代码
open class C { }
class D <: C { }
interface I<X> { }
I<D> <: I<C> 是不成立的(即使 D <: C 成立),这是因为在仓颉语言中,用户定义的类型构造器在其类型参数处是不型变的。
型变的具体定义为:如果 A 和 B 是(实例化后的)类型,T 是类型构造器,设有一个类型参数 X(例如 interface T<X>),那么
- 如果 T(A) <: T(B) 当且仅当 A = B,则 T 是不型变的。
- 如果 T(A) <: T(B) 当且仅当 A <: B ,则 T 在 X 处是协变的。
- 如果 T(A) <: T(B) 当且仅当 B <: A ,则 T 在 X 处是逆变的。
因为现阶段的仓颉中,所有用户自定义的泛型类型在其所有的类型变元处都是不变的,所以给定 interface I<X> 和类型 A、B,只有 A = B,我们才能得到 I<A> <: I<B>;反过来,如果知道了 I<A> <: I<B>,也可推出 A = B(内建类型除外:内建的元组类型对其每个元素类型来说,都是协变的;内建的函数类型在其入参类型处是逆变的,在其返回类型处是协变的。)
不型变限制了一些语言的表达能力,但也避免了一些安全问题,例如“协变数组运行时抛异常”的问题(Java 便有这个问题)。


4127

被折叠的 条评论
为什么被折叠?



