🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
虽然变异也在其它地方意义重大,但是它特别经常和类型参数一起出现,并像一个惊喜。此外,非常容易触发变异错误: > While variance is also relevant in other places, it occurs particularly often with type parameters and comes as a surprise in this context. Additionally, it is very easy to trigger variance errors: ~~~ class Base { public function new() { } } class Child extends Base { } class Main { public static function main () { var children = [new Child()]; // Array<Child> should be Array<Base> // Type parameters are invariant // Child should be Base var bases:Array<Base> = children; } } ~~~ 显然,一个 Array<Child> 不能被分配到一个 Array<Base>,即使 Child可以被分配到Base。原因可能比较意外:因为 array 可以被写入,如通过它们的 push() 方法。忽略变异错误非常容易产生问题: > Apparently,an `Array<Child>` cannot be assigned to an `Array<Base>`,even though `Child` can be assigned to `Base`. The reason for this might be somewhat unexpected: It is not allowed because arrays can be written to, e.g. via their `push()` method. It is easy to generate problems by ignoring variance errors: ~~~ class Base { public function new() { } } class Child extends Base { } class OtherChild extends Base { } class Main { public static function main () { var children = [new Child()]; // subvert type checker var bases:Array<Base> = cast children; bases.push(new OtherChild()); for(child in children) { trace(child); } } } ~~~ 这里我们使用 cast(第5.23节)破坏了类型检查,因此允许了注释行后的赋值。我们保存一个引用 bases 到原始的数组,类型为 Array<Base>。这使的推送另一个兼容Base的类型(OtherChild)到了数组。然而,我们原始的引用 children 仍然是 Array<Child>类型,当我们在迭代它的一个元素的时候遇到 OtherChild实例就会出现问题。 > Here we subvert the type checker by using a cast (5.23), thus allowing the assignment after the commented line. With that we hold a reference `bases` to the original array, typed as `Array<Base>`. This allows pushing another type compatible with `Base` (OtherChild) onto that array. However,our original reference `children` is still of type `Array<Child>` and things go bad when we encounter the `OtherChild` instance in one of its elements while iterating. 如果 Array 没有 push() 方法,没有其它修改的手段,赋值则变得安全,因为没有矛盾的类型被添加到它。在Haxe中,我们可以使用结构子类型化(第3.5.2节)相应的限制类型来实现这个。 > If Array had no `push()` method and no other means of modification,the assignment would be safe because no incompatible type could be added to it. In Haxe, we can achieve this by restricting the type accordingly using structural subtyping (3.5.2): ~~~ class Base { public function new() { } } class Child extends Base { } typedef MyArray<T> = { public function pop():T; } class Main { public static function main () { var a = [new Child()]; var b:MyArray<Base> = a; } } ~~~ 我们可以安全的分配 b 作为 MyArray<Base>类型,MyArray 只有一个 pop() 方法。没有为 MyArray定义可以用来添加冲突类型的方法,因此被认为是协变的。 > We can safely assign with `b` being typed as `MyArray<Base>` and `MyArray` only having a `pop()` method. There is no method defined on `MyArray` which could be used to add incompatible types, it is thus said to be covariant. **协变** >[warning] 定义:协变 一种复合类型,如果它的组成类型可以被分配为缺少特定组件,如它们是只读,不允许写时,则被认为是协变的。 >[warning] Definition: Covariance A compound type is considered covariant if its component types can be assigned to less specific components, i.e. if they are only read, but never written. >[warning] 定义:抗变性 一个复合类型,如果它的组件类型可以被分配得为缺少通用的组件,如它们只写,但是不读,则被认为是抗变。 >[warning] Definition: Contravariance A compound type is considered contravariant if its component types can be assigned to less generic components, i.e. if they are only written, but never read.