Dart中带有通用类型参数的回调函数

11

我试图定义一个回调函数,该函数需要接受一个通用参数并返回相同类型的值。请记住,以下示例是我实际需要的过度简化版本。

final T Function<T>(T value) self = (value) => value

这导致了以下错误,我似乎无法摆脱。
The argument type '(dynamic) → dynamic' can't be assigned to the parameter type '<T>(T) → T'

dart(argument_type_not_assignable)

唯一有效的方法似乎是为值赋予类型,但这违背了首次使用类型参数的初衷。
final T Function<T>(T value) same = <String>(value) => value;

我需要它是通用的,以便调用者可以传递期望返回的类型。我还需要将其存储在变量中,以便我可以将其作为回调函数传递。

如果直接不可能,你知道有什么解决方法吗?提前感谢你。


如果要求不清楚,这里是一个更完整的例子。

abstract class Provider<T> {
  T get bar;
}

class StringProvider extends Provider<String> {
  String get bar => 'bar';
}

class NumberProvider extends Provider<int> {
  int get bar => 42;
}

class Foo {
  final T Function<T>(Provider<T> provider) provide;

  const Foo({ this.provide });
}

test() {
  final foo = Foo(provide: (provider) => provider.bar);

  String strValue = foo.provide(StringProvider()); // should be 'bar'
  int numValue = foo.provide(NumberProvider()); // should be 42
}

令人烦恼的是,Dart实际上理解foo.provide(StringProvider())会返回一个字符串,并且使用NumberProvider确实会返回一个整数,但是,当变量实际上被赋值时,错误仍然会出现在该行。
final foo = Foo(provide: (provider) => provider.bar);

The argument type '(dynamic) → dynamic' can't be assigned to the parameter type '<T>(Provider<T>) → T'
2个回答

10
原来我可以通过在定义值时给出任何具体类型来欺骗类型检查器。请注意,dynamic不允许,但其他任何类型都可以。
final foo = Foo(provide: <int>(provider) => provider.bar);

这样做不仅可以消除错误,还可以在调用时使provide方法返回正确的类型。
总之,这似乎是类型检查器的一个简单缺陷,而不是使用已有语言特性实现不可能或困难的事情。我将在语言的GitHub存储库上提出问题,以便进一步调查和讨论。

更新 #1: 问题已在GitHub上开启。


更新 #2: 问题已解决,结果表明该行为是设计上的。引用SDK团队的Erik Ernst的话说:

试试这个: final foo = Foo(provide: <T>(Provider<T> provider) => provider.bar);!

问题在于您将非泛型函数传递给Foo构造函数,而应传递泛型函数。泛型函数类型和非泛型函数类型之间没有子类型关系,因此根据类型检查器,您可以传递一个字符串,这就是“无法分配”的原因。

结果表明,只需在参数列表前添加<T>(而不是原始解决方法中的int)即可解决此问题。
final foo = Foo(provide: <T>(provider) => provider.bar);

这会强制Dart理解provider的类型为Provider<T>,并且该方法将返回一个类型为T的值,使我们免于使用具体类型并仍然摆脱错误。

0

你需要定义一个独立的通用函数:

class Foo {
  final T Function<T>(Provider<T> provider) provide;

  const Foo({ this.provide });
}

main() {
  T f<T>(Provider<T> provider) => provider.bar;
  final foo = Foo(provide: f);

  String strValue = foo.provide(StringProvider()); // should be 'bar'
  int numValue = foo.provide(NumberProvider()); // should be 42
}

不幸的是,这定义了一个函数 - 例如,在类中使用的函数 - 而不是独立的变量。我需要它成为后者,以便我可以将其存储并作为回调函数传递。我已经在我的原始问题中添加了这个备注。 - skreborn
没有你的补充,我本来以为self是一个顶层变量。 - Alexandre Ardhuin
抱歉造成困惑,感谢您的提醒。您更新后的答案让我更接近解决问题,但还没有完全解决。给Foo添加类型参数可以消除错误,但这样做会导致foo.provide无论传递什么参数都只返回dynamic - skreborn
我已经添加了自己的答案,因为我找到了一种欺骗类型检查器的方法,从而得到了我所需的结果。无论如何,我很感谢你的帮助。 - skreborn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接