为什么在Dart中使用明确的“.cast<>()”函数而不是“as<>”

时间:2018-03-28 18:41:52

标签: dart

在我的问题Dart 2.X List.cast() does not compose中,答案需要将List<dynamic>转换为List<String>

List<String> ls = (json['data'] as List).cast<String>().map((s) => s.toUpperCase()).toList();

我从其他语言的经历让我先写了这个:

List<String> ls = (json['data'] as List<String>).map((s) => s.toUpperCase()).toList();

请注意,这会在Dart 2中编译但在运行时失败。

为什么对List进行Dart类型转换需要函数as List).cast<String>()而不是简单地使用Dart as“类型转换运算符”,例如as List<String>

----编辑----

我正在使用最新的Dart 2.0.0-dev.43.0并使用as类型转换/断言获得不一致的运行时行为。创建新迭代的.cast<>()函数不是与.map()相同吗?将我的代码更改为:

List<String> ls = (json['data'] as List).map((s) => (s as String).toUpperCase()).toList();

这似乎利用了List的第一次投射是List<dynamic>。因此.map函数参数也是dynamic

我上面的第二个例子as List<String>可以在我们的代码中的某些地方使用但不能在其他地方使用。请注意,IntelliJ正确地推断出所有上述示例中的类型 - 它是发生故障的运行时。我猜测不一致的行为是由于Dart 2.x仍在开发中。

---- 2nd Edit ----

以下是我在其中一个类构造函数中的测试用例:

Map<String, dynamic> json = { "data": ["a", "b", "c"] };

//List<String> origBroken = json["data"].map( (s) => s.toUpperCase() ).toList();

// Sometimes works - sometimes gives "Ignoring cast fail from JSArray to List<String>" at runtime!!!
List<String> wonky = (json["data"] as List<String>).map( (s) => s.toUpperCase() ).toList();
print("Wonky $wonky");

List<String> fix1 = (json["data"] as List).cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> fix2 = (json["data"] as List).map( (s) => (s as String).toUpperCase() ).toList();
List<String> explicit2 = (json["data"] as List<dynamic>).map( (dynamic s) => (s as String).toUpperCase() ).toList();

// From accepted answer of the linked question - compile error because .cast() doesn't take parameters
//   error: Too many positional arguments: 0 expected, but 1 found.
//List<String> notBroken = (json['data'] as List).cast<String>((s) => s.toUpperCase()).toList();
List<String> notBrokenFixed = (json['data'] as List<String>).cast<String>().map((String s) => s.toUpperCase()).toList();

问题是Ignoring cast fail from JSArray to List<String>分配有时会发出警告wonky。当我有时说这是因为当我对使用包含此代码的库的主应用程序进行更改时,它会发生不可预测的变化 - 无需更改此类甚至库。

在我编写上面的第一个编辑时,wonky无效。我现在再次尝试它,它正在工作。我没有更改这个库中的任何代码 - 我一直在主应用程序中工作,它依赖于这个代码的库。

有些背景,这是一个从Angular / Typescript转换而来的多库项目。这些测试用例基于我们将JSON反序列化为Dart类的处理。我们将JSON(动态)字符串映射到各种数据结构,例如枚举,选项&lt;&gt;和任何&lt;&gt; (来自dartz)使用类构造函数初始化器。

几个星期前,运行时警告开始发生,我相信因为Breaking Change: --preview-dart-2 turned on by default。 我知道这个警告很快就会出错。因此,我将警告追溯到这些来自JSON动态数据的转换(是的,动态数据是Dart中的边缘情况,但它是dart:convert提供的内容)。

我们正在使用DDC开发Mac,最新的Dart 2.0.0-dev.43.0,角度5.0.0-alpha + 8,build_runner 0.8.0,IntelliJ 2018.1,并在Chrome 65.0.3325.181上运行。

----最终编辑----

此问题背后的当前开发构建/运行时存在不稳定性。不,我没有可重复的例子。更改和重建我们的主应用程序将导致未修改的库依赖项中的此代码有时会提供运行时警告Ignoring cast fail from JSArray to List<String>

此问题原始部分的可疑代码(上面也是wonky

List<String> ls = (json['data'] as List<String>).map((s) => s.toUpperCase()).toList();

将动态JSON数据强制转换为List<String>。类型完全受约束,Dart分析器/ IntelliJ推断sStatic type: String

有时会发生运行时警告以及使用.cast()的相关答案是导致此问题的原因。此时我会相信分析器并忽略运行时警告。

1 个答案:

答案 0 :(得分:6)

在Dart 2中,通用类型具体化。

as ...更像是一个断言,如果值类型不匹配as会导致运行时异常。

cast<T>()是在Iterable上的Dart 2中引入的一种方法,它实际上创建了一个新的可迭代类型Iterable<T>(或者在您的情况下是子类List<T>)原始可交换的值。

<强>更新

您可以使用print('wonky: ${wonky.runtimeType}');查看实际类型。

如果类型符合您的要求,您可以使用as将其传达给分析人员,以便他们可以安全地使用此类型。

如果类型不匹配,例如因为它是List而不是List<String>,那么您可以使用.cast<String>()实际使其成为List<String>

List<String> notBroken = (json['data'] as List).cast<String>((s) => s.toUpperCase()).toList();

在这里,您似乎尝试使用cast进行投射和映射,但这无效。 map()可以做到这两点

List<String> notBroken = (json['data'] as List).map<String>((s) => s.toUpperCase()).toList();