我正在尝试定义一个需要接受泛型参数并返回相同类型值的回调函数。请记住,以下示例是我实际需要的过度简化版本。
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'
事实证明,我可以通过在定义值时给出任何具体类型来欺骗类型检查器。请注意,不允许使用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<T>
,并且该方法将返回类型为T
的值,从而使我们无法使用具体类型并仍然摆脱错误。
您必须定义一个独立的通用函数:
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
}