在 Dart 中,如果重写超类方法,则重写方法的参数必须具有
与原件类型相同。
Since Animal.chase
在你的例子中接受一个参数Animal
,您必须在覆盖中执行相同的操作:
class Cat extends Animal {
@override
void chase(Animal x) { ... }
}
为什么?想象一下,如果没有这样的限制。Cat
可以定义void chase(Mouse x)
while Dog
可以
定义void chase(Cat x)
。然后想象你有一个List<Animal> animals
你打电话chase(cat)
在
其中之一。如果动物是狗,它会起作用,但如果动物是猫,猫就不是老鼠!那只猫
类无法处理被要求追逐另一只猫的情况。
所以你被迫使用void chase(Animal x)
。我们可以模拟一个void chase(Mouse x)
类型签名
通过添加运行时类型检查:
void chase(Animal x) {
if (x is Mouse) {
/* do chase */
} else {
/* throw error */
}
}
事实证明这是一个相当常见的操作,如果能在编译时检查就更好了
在可能的情况。所以 Dart 添加了一个covariant
操作员。将函数签名更改为chase(covariant Mouse x)
(其中 Mouse 是 Animal 的子类)做了三件事:
- 允许您省略
x is Mouse
检查一下,因为它已经为您完成了。
- 如果任何 Dart 代码调用,则会产生编译时错误
Cat.chase(x)
其中 x 不是 Mouse 或其子类 - 如果在编译时已知。
- 在其他情况下会产生运行时错误。
另一个例子是operator ==(Object x)
对象上的方法。假设你有课Point
:
你可以实施operator==
这边走:
class Point {
final int x, y;
Point(this.x, this.y);
bool operator==(Object other) {
if (other is Point) {
return x == other.x && y == other.y;
} else {
return false;
}
}
}
但即使你比较这段代码也能编译Point(1,2) == "string"
或一个数字或其他一些物体。将点与非点进行比较是没有意义的。
您可以使用covariant
告诉 Dartother
应该是一个点,否则会出错。这可以让你放弃other is Point
也有一部分:
bool operator==(covariant Point other) =>
x == other.x && y == other.y;
为什么称为“协变”?
协变是一个奇特的类型理论术语,但它基本上意味着“这个类或其子类”。换句话说,它意味着类型
在类型层次结构中等于或较低的。
您明确告诉 Dart 将此参数的类型检查加强为subclass原来的。
第一个例子:将Animal收紧为Mouse;第二个:将对象收紧到点。
有用的相关术语有逆变,这意味着类型层次结构中等于或更高的类型,并且不变的,
这正是这种类型的意思。
了解更多信息,这个堆栈溢出问题 https://stackoverflow.com/questions/2662369/covariance-and-contravariance-real-world-example是一个很好的资源。