在Dart中使用通用类型参数进行回调

时间:2019-04-25 11:17:45

标签: generics dart type-parameter dart-2

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

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 个答案:

答案 0 :(得分:1)

事实证明,我可以通过在定义值时提供任何具体类型来欺骗类型检查器。请注意,dynamic是不允许的,但是其他任何事情都会发生。

final foo = Foo(provide: <int>(provider) => provider.bar);

这既可以消除错误,又可以让provide方法在调用时返回正确的类型。

总而言之,这似乎是类型检查器的一个简单缺陷,并不是使用现有语言功能实际上不可能或很难实现的事情。我将在该语言的GitHub存储库上提出一个问题,以便进行进一步的调查和讨论。


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


更新#2 :问题已解决,事实证明该行为是设计使然。引用Erik Ernst(来自SDK团队):

  

尝试一下:final foo = Foo(provide: <T>(Provider<T> provider) => provider.bar);

     

问题在于,您要将非泛型函数传递给Foo构造函数,而应该传递泛型函数。泛型函数类型和非泛型函数类型之间没有子类型关系,因此根据类型检查器,您最好也传递一个String,这就是“无法分配给”消息的原因。

事实证明,只需在参数列表之前添加<T>(而不是原始解决方法中的int)即可解决此问题。

final foo = Foo(provide: <T>(provider) => provider.bar);

这迫使Dart理解提供者的类型为Provider<T>,并且该方法将返回类型为T的值,从而使我们免于使用具体类型,并且仍然摆脱了错误。 / p>

答案 1 :(得分: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
}