Dart:使用通用扩展方法,得到 NoSuchMethodError: Class 'xxx' has no instance method 'yyy'

hzq*_*elf 6 dart

实现 Kotlin 标准库扩展函数let

extension KtStd<T, R> on T {
  R let(R f(T it)) => f(this);
}
Run Code Online (Sandbox Code Playgroud)

并编写一个 CLI sum 计算器(问题不能简化),期望:

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

input: apple 1.2 banana 3.4
output: 4.6
Run Code Online (Sandbox Code Playgroud)

然后代码:

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));
}
Run Code Online (Sandbox Code Playgroud)

输入时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)
Run Code Online (Sandbox Code Playgroud)

此外,如果我将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)
Run Code Online (Sandbox Code Playgroud)

飞镖版本是:

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

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

jam*_*lin 3

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);
}
Run Code Online (Sandbox Code Playgroud)

您打算KtStd<T, R>依赖于letconstrain R,但这是倒退的。如果没有首先在 上进行延期,let则不是合法的呼叫。因此 是不受约束的并且被假定为。这也迫使我们返回。KtStd<T, R>TRdynamicletdynamic

如果可能,我建议恢复到早期版本,其中let单独通用R

extension KtStd<T> on T {
  R let<R>(R f(T it)) => f(this);
}
Run Code Online (Sandbox Code Playgroud)

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

analyzer:
  strong-mode:
    implicit-casts: false
    implicit-dynamic: false
  language:
    strict-raw-types: true
Run Code Online (Sandbox Code Playgroud)