繁体   English   中英

为什么 @override 方法的参数类型在 Dart 中不能“拧紧”?

[英]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 AnimalCat为例, 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扩展了AnimalCat只是限制了可以将哪种动物传递给chase

但是想想多态性会发生什么。 硬性规则是任何扩展和覆盖基类的类都必须与该基类兼容。 这确保了,即使在键入基类的句柄上调用函数,也可以保证调用将转到覆盖类中的正确函数定义。

这就是使抽象类成为可能的原因——如果派生类中的函数实现与基类中的相同函数不兼容,则不可能将继承类的实例视为基类。

例如,以文章中的示例为例,但不是显式创建Cat ,而是让它成为一些随机生成的动物:

Animal a = RandomAnimalFactory.create();
a.chase(Alligator());

a的确切类型是什么? 你没有办法知道。 你只知道它是Animal的某个子类型。

现在看看对a.chase(Alligator())的调用。 那会成功吗? 嗯,是的,因为Animal.chase需要一个AnimalAlligator是一个Animal 那里没有问题。

但是想象一下子类是否可以限制该参数的类型,例如,如果Cat可以将其限制为Mouse 现在还不清楚该调用是否会成功,因为即使您知道aAnimal ,您也不知道是哪种Animal 如果它是一种使Alligator不再是有效参数的类型怎么办? 为了知道,类型系统必须解包变量的声明类型以检查实际类型,然后才知道调用是否成功。 这与继承背道而驰,继承基类型的类型可以与它的任何兄弟类型互换。

一下子,原本简单的事情变得异常复杂。 它不仅使基于 OOP 的类型系统的实现更加复杂,而且完全破坏了继承的意义。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM