[英]Dart: doesn't override generic method has generic parameter type similar to the return type
[英]Why can't the parameter type of a @override method be “tighten” in Dart?
我是 Dart 新手,最近我一直在阅读一些关于 Dart 声音类型系统的文档,直到我遇到了一个名为Use sound parameter types when overriding methods
它指出
重写方法的参数必须具有相同类型或超类中相应参数的超类型。 不要通过将类型替换为原始参数的子类型来“收紧”参数类型。
我的问题是,当然,通过在覆盖方法中禁止参数类型“收紧”,我们可以防止
定义一只猫并在鳄鱼之后发送它
class Animal {
chase(Animal a) {}
}
class Mouse extends Animal {}
class Alligator extends Animal {}
class CatA extends Animal {
@override
// error on this line
void chase(Mouse x) {} // It makes sense. Cat chases Mouse
}
class CatB extends Animal {
@override
// error on this line
void chase(Alligator x) {} // Cat chases Alligator ? You can't be serious!
}
但是如何使用Object
(witch 是几乎所有其他对象的超类型)作为其参数类型
class Cat extends Animal {
@override
void chase(Object x) { // Now this Cat can chase anything.....
}
}
void main() {
var cat = Cat();
cat.chase(Alligator()); // Again, we are trying to let a little pussy chase a terrifying beast!
}
// This piece of code works
这到底是怎么回事,这对我来说根本没有意义......此外,如果我创建一个扩展Cat
的超级猫,它确实可以追逐Alligator
class SuperCat extends Cat {
@override
// error on this line
void chase(Alligator x) { // I make a SuperCat chasing after a Alligator intentionally, but it doesn't work...
}
}
上面的这些事情真的让我大吃一惊,我是不是在某种程度上弄错了,还是在引擎盖下还有什么东西可以做到这一点?
感谢@jamesdlin 和@Abion47,我终于可以弄清楚大部分谜题,但还有一个问题需要解决。 正如@jamesdlin 提到的,在覆盖方法时必须遵守contract of the base class method
。 以Animal
和Cat
为例, Animal.chase
制定了一个约定, chase
必须兼容接受任何Animal
,无论是Alligator
还是Mouse
,但是这个约定是否也做了一个限制, chase
必须不能接受任何其他Object
除了Animal
? (很自然地想到它,因为你不能将随便的Object
参数传递给Animal.chase
)如果是这样,为什么 Dart 允许将Cat.chase
参数类型从Animal
扩大到Object
? 这不违反Animal.chase
的合同吗?
jamesdlin 引用的答案详细说明了为什么不允许这样做,但我会尝试给出一个简明扼要的版本。
abstract class Animal {
void chase(Animal a) { ... }
}
class Mouse extends Animal {}
class Alligator extends Animal {}
class Cat extends Animal {
@override
void chase(Mouse x) { ... } // No-no
}
从理论上讲,这没问题,因为Mouse
扩展了Animal
而Cat
只是限制了可以将哪种动物传递给chase
。
但是想想多态性会发生什么。 硬性规则是任何扩展和覆盖基类的类都必须与该基类兼容。 这确保了,即使在键入基类的句柄上调用函数,也可以保证调用将转到覆盖类中的正确函数定义。
这就是使抽象类成为可能的原因——如果派生类中的函数实现与基类中的相同函数不兼容,则不可能将继承类的实例视为基类。
例如,以文章中的示例为例,但不是显式创建Cat
,而是让它成为一些随机生成的动物:
Animal a = RandomAnimalFactory.create();
a.chase(Alligator());
a
的确切类型是什么? 你没有办法知道。 你只知道它是Animal
的某个子类型。
现在看看对a.chase(Alligator())
的调用。 那会成功吗? 嗯,是的,因为Animal.chase
需要一个Animal
而Alligator
是一个Animal
。 那里没有问题。
但是想象一下子类是否可以限制该参数的类型,例如,如果Cat
可以将其限制为Mouse
。 现在还不清楚该调用是否会成功,因为即使您知道a
是Animal
,您也不知道是哪种Animal
。 如果它是一种使Alligator
不再是有效参数的类型怎么办? 为了知道,类型系统必须解包变量的声明类型以检查实际类型,然后才知道调用是否成功。 这与继承背道而驰,继承基类型的类型可以与它的任何兄弟类型互换。
一下子,原本简单的事情变得异常复杂。 它不仅使基于 OOP 的类型系统的实现更加复杂,而且完全破坏了继承的意义。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.