Dart 中 Future 的向下泛型
我有一个具有通用参数的 future,它是另一个类 (B extends A
) 的超类 (A
)。我知道 Future 的值的实例是子类型。为什么我不能在 dart 中将 Future
向下转换为 Future
?如果我打开 Future 一次,然后使用 async/await 再次包装它,它就可以工作。
这是一个例子:
class A {}
class B extends A{}
void main() {
Future<A> getFuture() async { return B();}
Future<B> getBasA() { return getFuture() as Future<B>;}
Future<B> getBasAasync() async { return (await getFuture()) as B;}
print(getBasAasync()); // Works
print(getBasA()); // Throws at runtime
}
为了好奇并作为问题的动机,这里有一个更贴近现实的例子。我有一个发出数据包的流,我对其进行过滤,然后得到第一个数据包,如下所示:
Future<T> getResponse<T extends ReceivedPacket>() =>
getStream<ReceivedPacket>().firstWhere((packet) => packet is T) as Future<T>; //throws
Future<T> getResponse<T extends ReceivedPacket>() async { //works
return (await getStream<ReceivedPacket>().firstWhere((packet) => packet is T)) as T;
}
PS:我已经在 Typescript (将愉快地编译和运行)和 C# (无法编译,但我的 C# 知识非常有限)。我知道这个问题的答案可能是“因为这就是 dart 类型系统的工作原理”。我只是很困惑,因为我预计它要么像 C# 一样在编译时失败,要么像打字稿一样在运行时工作。
I have a future that has a generic parameter, which is a superclass (A
) of another class (B extends A
). I know for a fact that the instance of the value of the Future is of the subtype. Why can't I downcast the Future<A>
to Future<B>
in dart? If I unwrap the Future once and then wrap it again using async/await, it works.
Here's an example:
class A {}
class B extends A{}
void main() {
Future<A> getFuture() async { return B();}
Future<B> getBasA() { return getFuture() as Future<B>;}
Future<B> getBasAasync() async { return (await getFuture()) as B;}
print(getBasAasync()); // Works
print(getBasA()); // Throws at runtime
}
For the curious and as a motivation for the question, here's a closer-to-world example. I have a stream that emits data packets, which I filter and then get the first like this:
Future<T> getResponse<T extends ReceivedPacket>() =>
getStream<ReceivedPacket>().firstWhere((packet) => packet is T) as Future<T>; //throws
Future<T> getResponse<T extends ReceivedPacket>() async { //works
return (await getStream<ReceivedPacket>().firstWhere((packet) => packet is T)) as T;
}
PS: I've tried it out in Typescript (will happily compile and run) and C# (won't compile, but I have very limited C# knowledge). I understand that the answer to this question might be "because this is how the dart type system works". I'm just confused, because I'd have expected it either to fail at compile time like C# or work at runtime, too, like typescript.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您将
getFuture()
声明为返回Future
但使用了async
关键字,因此 Dart 会自动转换return B();
(本质上)返回 Future
.value(B());
。返回的Future
从来就不是Future
,因此不能向下转型为它。显式创建
Future
将会达到您的预期:您可能会认为 Dart 在其中
async
函数中转换return x;
时,它应该创建一个 < code>FutureT
是x
的静态类型,而不是从函数的返回类型推断它。正如lrn在评论中解释的那样,这是不可能的,因为你可能有:调用者必须立即返回一个
Future
,但不知道它的值是否是一个B
或C
直到Future
最终完成。我对 C# 的经验也非常有限,但我认为 C# 会给你一个编译错误,因为 C# 不考虑
Generic
是Generic
的子类型,而 Dart 则是。You declared
getFuture()
as returningFuture<A>
but with theasync
keyword, so Dart automatically transformsreturn B();
to (essentially)return Future<A>.value(B());
. The returnedFuture
was never aFuture<B>
and therefore cannot be downcast to it.Creating the
Future
explicitly would do what you expect:You could argue that Dart when transforms
return x;
inasync
functions, it should create aFuture<T>
whereT
is the static type ofx
instead of inferring it from the function's return type. As lrn explained in comments, that can't be done because you might have:The caller must get a
Future
back immediately, but it won't be known whether its value will be aB
orC
until theFuture
eventually completes.I too have very limited experience with C#, but I think that C# gives you a compilation error because C# does not consider
Generic<SubType>
to be a subtype ofGeneric<SuperType>
whereas Dart does.