飞镖:使用通用扩展方法,获取NoSuchMethodError:类“ xxx”没有实例方法“ yyy”

时间:2020-05-20 19:45:14

标签: dart

实施Kotlin标准库扩展功能let

extension KtStd<T, R> on T {
  R let(R f(T it)) => f(this);
}

并编写一个CLI和计算器(无法简化问题),期望:

input: 1 2 3 4 5 6 7 8 9
output: 45

input: apple 1.2 banana 3.4
output: 4.6

然后输入代码:

main() {
  stdin.readLineSync(encoding: Encoding.getByName('utf-8')).split(" ")
      .map((s) => double.tryParse(s)).where((e) => e != null).fold(0, (acc, i) => acc + i)
      .let((d) => d % 1 == 0 ? d.toInt() : d)
      .let((it) => print(it));
}

输入1 2 3时收到错误消息:

Unhandled exception:
NoSuchMethodError: Class 'double' has no instance method 'let'.
Receiver: 6.0
Tried calling: let(Closure: (dynamic) => dynamic)
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1      main (file:xxx.dart:xx:xx)
#2      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

此外,如果我将fold函数更改为reduce,则输入相同,得到错误消息:

Unhandled exception:
NoSuchMethodError: Class 'int' has no instance method 'let'.
Receiver: 6
Tried calling: let(Closure: (dynamic) => void)
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1      main (file:xxx.dart:xx:xx)
#2      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

飞镖版本是:

Dart VM version: 2.8.2 (stable) (Mon May 11 15:06:42 2020 +0200) on "windows_x64"

为什么我收到这些错误消息?我该怎么解决?

3 个答案:

答案 0 :(得分:2)

Dart扩展名是静态。它们是语法糖,适用于编译时已知的类型。这意味着扩展不能在运行时确定类型的dynamic值上使用。

您的长链表达式最终可能会使用dynamic类型,而这些类型可能不在您所期望的位置。一个问题是,当您执行.fold(0, (acc, i) => acc + i)时,不会推导出回调的参数和返回类型。 (请参见https://github.com/dart-lang/language/issues/731。)因此acc和返回类型被假定为dynamic类型。

您可以通过为fold.fold<double>(...)明确指定类型来解决此问题。


在代码的编辑版本中,您引入了第二个问题:

extension KtStd<T, R> on T {
  R let(R f(T it)) => f(this);
}

您打算让KtStd<T, R>依靠let来约束R,但这是倒退的。在未首先将let扩展为KtStd<T, R>的情况下,T不是合法电话。 R因此不受约束,并假定为dynamic。然后,这也迫使let也返回dynamic

如果可能,我建议恢复到您的较早版本,其中letR上是单独通用的:

extension KtStd<T> on T {
  R let<R>(R f(T it)) => f(this);
}

通过修改analysis_options.yaml文件并进行设置,您可以在分析过程中更轻松地识别此类错误:

analyzer:
  strong-mode:
    implicit-casts: false
    implicit-dynamic: false
  language:
    strict-raw-types: true

答案 1 :(得分:0)

如果您要坚持使用泛型,这也可以:

extension KtStd<T> on T {
  R let<R>(R Function(T) function) => function(this);
}

您的扩展方法也可以用这种方式编写(请参见lint):

extension KtStd<T> on T {
  R let<R>(R Function(dynamic) f) => f(this);
}

我实际上无法解释为什么,但是您需要指定功能参数的类型以使其正常工作

答案 2 :(得分:-1)

尝试对象

extension KtStd on Object {
  Object let(Object f(it)) => f(this);
}

void main() {
  "12"
      .let((it) => it.toString() + "3")
      .let((it) => int.parse(it) + 1)
      .let((it) => print(it)); //124
}