【问题标题】:Flutter Future<dynamic> vs. Future<String> subtype error?Flutter Future<dynamic> 与 Future<String> 子类型错误?
【发布时间】:2023-12-11 21:44:01
【问题描述】:

我刚刚更新了 Flutter 并成功从git 下载了我的原始项目。现在我收到了一个奇怪的Future 错误。我在 github 上看到在线提到它,但没有关于如何修复的明确答案。该项目甚至没有加载。它从我的 main.dart 文件中读取 Future 语句并返回...

[VERBOSE-2:dart_error.cc(16)] 未处理的异常:类型 “未来动态”不是“未来字符串”类型的子类型,其中
未来来自 dart:async
未来来自 dart:async
字符串是 来自飞镖:核心

*** 不确定错误在哪里。我的飞镖分析说“只等待期货”。这是我在小部件开始构建之前运行的期货代码...

Future<Null> getData() async{
    FirebaseUser user = await FirebaseAuth.instance.currentUser;
    user = FirebaseAuth.instance.currentUser;
    var userid = user.uid;
    fb.child('users/${userid}').onValue.listen((Event event) {
      if (event.snapshot.value != null) {
        name = event.snapshot.value['displayName'];
        image = event.snapshot.value['image'];
      } else {
        name = "User";
      }
    });
  }

【问题讨论】:

  • 我今天有一个类似的问题,我不得不将我的 List> (以前可以工作)更改为 List 即使它肯定包含 Maps (错误我得到的是 _InternalLinkedHashMap 不是 Map 的子类型,如果我没记错的话)。在我最终使用列表的地方,这段代码可以正常工作for (Map&lt;String, dynamic&gt; x in y)。我要么不太了解强模式的细微差别,要么某个地方出现了错误。你能发布代码 sn-p 为你触发这个吗?
  • 能否包含堆栈跟踪的一部分,其中包含引发错误的行?
  • 在您发布一些可能显示问题所在的代码之前,我们无能为力。如果项目在 github 和 public 上,一个链接就足够了。但是你应该检查你在哪里使用期货 - 确保它们都是 Future 或 Future 等 - 如果你有一个只是 Future 它可能以前工作过但不会在强模式下。
  • @rmtmckenzie 我添加了一些代码。真的不知道它发生在哪里,因为这个函数在我的 App Build 之前运行
  • @Charles 问题在于,您在第一行等待的不是未来。不过,这似乎与您最初描述的问题不同。

标签: asynchronous dynamic dart future flutter


【解决方案1】:

好的。我想我明白了。

Dart 1.0,是一种软类型语言,类似于

main() async {  
  print(await getData()); 
}

Future<Null> getDatare() async{return "a";} 

将打印“a”

问题是 Dart 2.0 是一种类型化语言,所以 Future 实际上是有意义的。而这里发生的事情(简而言之)是 Future 变成了 FutureNull,而你无法从 FutureNull 中获取 String(因为它只包含 Null)。

其实这种咬过我一次[1],但仔细想想,还是有道理的。看下面的代码:

List<dynamic> l = ["a", "b", "c", "d"];
List<String> d = l;

甚至更多

List<int> l = [1,2,3,4];
List<double> d = l;

会在 Flutter 中崩溃。为什么?因为想想这里发生了什么:

什么是“l”?

----------------------
| int | int | int | int |
----------------------

什么是“d”?

---------------------------------------------
| double | double | double | double |
---------------------------------------------

那么如何在“l”和“d”之间进行转换呢?

您必须创建一个新的双精度列表,然后复制 l 中的值,将其转换为双精度,然后将其存储在“d”中。

但这不仅仅适用于列表。它适用于所有泛型。

当你有类似A&lt;B&gt; 的东西时,它是一个A&lt;C&gt; 完全不同的类型,你不能简单地从一个转换到另一个,出于同样的原因:

取以下代码:

class Box<T> {
  T a;
  Box(T v) {
    a=v;
  }
  T getVal() {
    return a;
  }
  }

现在将 Box 转换为 Box。没有意义,对吧?我可以执行以下操作吗?

Box<int> i = new Box(5);
Box<String> s= new Box("a");
i + (s as Box<int>)

所以这里真正发生的是 Box 变为 BoxInt,Box 变为 BoxString(编译器将标识符“T”替换为“int”或“T”替换为“String”)。

所以你在这里有同样的事情:你必须从未来拆箱价值,然后使用它。在您的情况下(实际上,您的情况还有一个问题 - 您没有返回任何内容)您可能想要一个 Future,然后 await 那个 Future 并获得一个字符串。


[1]。它怎么咬我的?来自 Kotlin(或任何其他 JVM 语言),我习惯了能够做类似的事情:

List<A> a = ...;
List<B> b = a;

没有问题。为什么?因为 JVM 没有真正的泛型。它所做的称为“类型擦除”,真正发生的情况是:

List<Object> a = ...;
List<Object> b = a;

嗯,这显然有效,因为所有Lists 都持有一个指向对象的指针,在运行时确定。

那是因为 Java 1.0(与 Dart 不同)从来没有泛型并且它们是固定的,所以为了向后兼容,它们使泛型的类型安全性低于它们本来可以的。

所以 Dart(或 C++ 或 Rust 或 Go 数组)将泛型视为单态,而 Java 将它们视为多态代码(将“T”视为指针)。

【讨论】:

  • 所以。我应该使用 Future 而不是使用 Future?并返回我预期的对象?
  • @CharlesJr 你应该创建一个 User 类并返回一个 Future