Return 类型为 "void" 时 Dart 中的方法覆盖

Method Overriding in Dart when the Return Type is "void"

我预计在没有 correct(据我所知)return 类型的情况下覆盖 Dart 中的方法时会出现编译时错误。但是,当方法的 return 类型为 void:

时,情况似乎有所不同
abstract class AbstractClass {
  void methodDoesNotReallyReturnVoid();
}

class ConcreteClass extends AbstractClass {
  // NO ERROR here!
  @override
  int methodDoesNotReallyReturnVoid() => 12;
}

void main() {
  print(ConcreteClass().methodDoesNotReallyReturnVoid()); // hm...?
}

上面的代码没有抱怨地编译并按预期打印整数,尽管我没有正确覆盖该方法,例如 Java.

请问这里的“void”有什么区别?

这是结合 Formal specification (Dart 2.2) 的某些部分后的答案。

首先,语言规范中定义了一个方法被正确覆盖的条件。在问题中描述的混乱范围内,这里是重要的 section

If m and m' are both methods or both setters: Let F be the function type of m except that the parameter type is the built-in class Object for each parameter of m which has the modifier covariant. Let F' be the function type of m'. F must then be a subtype of F'.

这意味着考虑中的 return 类型 也可以是子类型 。下面的代码片段应该清楚:

abstract class AbstractClass {
  num methodReturnsNum();
}

class ConcreteClass extends AbstractClass {
  // No error here because int is a subtype of num
  @override
  int methodReturnsNum() => 12;
}

void main() {
  // Compiles and prints the integer.
  print(ConcreteClass().methodReturnsNum());
}

那么按照最初的问题,是否意味着 intvoid 的子类型?这对我来说是新的,the answer 是“是”:

Every type is a subtype of Object, every type is a subtype of dynamic, and every type is a subtype of void. Note that this implies that these types are equivalent according to the subtype relation.

因此,问题中的代码类似于:

abstract class AbstractClass {
  Object methodReturnsObject();
}

class ConcreteClass extends AbstractClass {
  // No error here because int is a subtype of Object
  @override
  int methodReturnsObject() => 12;
}

void main() {
  // Compiles and prints the integer.
  print(ConcreteClass().methodReturnsObject());
}

关于惊喜元素,Void Soundness 下的一个片段有注释:

In particular, we could require that method overrides should never override return type Object by return type void, or parameter types in the opposite direction; parameterized types with type argument void could not be assigned to variables where the corresponding type argument is anything other than void, etc. etc. But this would be quite impractical. In particular, the need to either prevent a large number of type variables from ever having the value void, or preventing certain usages of values whose type is such a type variable, or whose type contains such a type variable, that would be severely constraining on a very large part of all Dart code. So we have chosen to help developers maintain this self-imposed discipline in simple and direct cases, and leave it to ad-hoc reasoning or separate tools to ensure that the indirect cases are covered as closely as needed in practice.

引用了具体的细节,但是一个简短的版本是:

int methodDoesNotReallyReturnVoid() 重写能否满足基础 void methodDoesNotReallyReturnVoid() 方法的 contract?是的,因为可以简单地忽略返回值。