在 Dart 中,类型为 (int) => String 的函数不能用 int 调用

In Dart, a function of type (int) => String can't be called with int

我有以下代码:

typedef Eater<T> = String Function(T value);

Eater<T> eaterFor<T>(T value) {
  // Find an appropriate eater.
  if (value is int) {
    return ((int value) => 'Eating int $value.') as Eater<T>;
  } else if (value is String) {
    return ((String value) => 'Eating String $value.') as Eater<T>;
  }
  throw 'No eater found for $value.';
}

extension on Object? {
  void eat() {
    final eater = eaterFor(this);
    print(eater(this)); // This fails.
  }
}

void main() => 4.eat();

我预计这会打印 Eating int 4.,但是带有 print 的行失败并显示以下消息(在 DartPad 上,可能在 VM 上略有不同):

Closure 'eaterFor_closure': type '(int) => String' is not a subtype of type '(Object?) => String'

显然,(int) => String 类型的闭包不能用 this 调用,它是 int 类型。 我认为这应该能以某种方式工作,但显然,我对 Dart 工作方式的心智模型与编译器不匹配。

我在这里错过了什么?我们有一个闭包和一个匹配其输入变量类型的值。 我知道有很多转换和可能不完全类型安全的部分,但我们应该能够用值调用闭包,不是吗?

这是我到目前为止的尝试,但没有奏效:

为什么会抛出错误? 我怎样才能用 this 调用 eater

泛型和扩展由编译器而非运行时静态确定。你的问题是:

extension on Object? {
  void eat() {
    final eater = eaterFor(this);
    print(eater(this)); // This fails.
  }
}

实际编译为:

extension on Object? {
  void eat() {
    final eater = eaterFor<Object?>(this);
    print(eater(this)); // This fails.
  }
}

如果类型在运行时可以更精确,编译器只能猜测类型 T 必须是 Object? 事件。这会导致您得到:

Eater<Object?> eaterFor(Object? value) {
  // Find an appropriate eater.
  if (value is int) {
    return ((int value) => 'Eating int $value.') as Eater<Object?>;
  } else if (value is String) {
    return ((String value) => 'Eating String $value.') as Eater<Object?>;
  }
  throw 'No eater found for $value.';
}

这不是真的有效,因为:

String Function(Object? value);

可以将更多类型的值作为输入,而不仅仅是 int。所以你的转换失败,因为 ((int value) => 'Eating int $value.') 不能转换为 String Function(Object? value).

你不应该另外投射参数

Eater<T> eaterFor<T>(T value) {
  // Find an appropriate eater.
  if (value is int) {
    return ((T value) => 'Eating int $value.');
  } else if (value is String) {
    return ((T value) => 'Eating String $value.');
  }
  throw 'No eater found for $value.';
}