在飞镖中向下转换 Future 的泛型
Downcasting generic of Future in dart
我有一个具有通用参数的未来,它是另一个 class (B extends A
) 的超级 class (A
)。我知道 Future 值的实例属于子类型。为什么我不能在 dart 中将 Future<A>
向下转换为 Future<B>
?如果我打开 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 (will happily compile and run) and C# 中尝试过了(不会编译,但我的 C# 知识非常有限)。我知道这个问题的答案可能是“因为飞镖类型系统就是这样工作的”。我只是很困惑,因为我原以为它要么像 C# 那样在编译时失败,要么像打字稿一样在运行时工作。
您将 getFuture()
声明为 returning Future<A>
但使用了 async
关键字,因此 Dart 会自动将 return B();
转换为(本质上) return Future<A>.value(B());
. returned Future
从来都不是 Future<B>
,因此不能向下转换为它。
显式创建 Future
会如您所愿:
Future<A> getFuture() { return Future<B>.value(B()); }
你可能会争辩说 Dart 在 async
函数中转换 return x;
时,它应该创建一个 Future<T>
,其中 T
是 x
的静态类型而不是从函数的 return 类型推断它。正如 lrn 在评论中解释的那样,这是不可能的,因为你可能有:
class C extends A {}
Future<A> getFuture() async {
await Future.delayed(const Duration(seconds: 1));
if (Random().nextBool()) {
return B();
} else {
return C();
}
}
调用者必须立即得到一个Future
,但直到Future
最终才知道它的值是B
还是C
完成。
I'd have expected it either to fail at compile time like C#
我对 C# 的经验也非常有限,但我认为 C# 会给你一个编译错误,因为 C# 不认为 Generic<SubType>
是 Generic<SuperType>
的子类型,而 Dart 会。
我有一个具有通用参数的未来,它是另一个 class (B extends A
) 的超级 class (A
)。我知道 Future 值的实例属于子类型。为什么我不能在 dart 中将 Future<A>
向下转换为 Future<B>
?如果我打开 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 (will happily compile and run) and C# 中尝试过了(不会编译,但我的 C# 知识非常有限)。我知道这个问题的答案可能是“因为飞镖类型系统就是这样工作的”。我只是很困惑,因为我原以为它要么像 C# 那样在编译时失败,要么像打字稿一样在运行时工作。
您将 getFuture()
声明为 returning Future<A>
但使用了 async
关键字,因此 Dart 会自动将 return B();
转换为(本质上) return Future<A>.value(B());
. returned Future
从来都不是 Future<B>
,因此不能向下转换为它。
显式创建 Future
会如您所愿:
Future<A> getFuture() { return Future<B>.value(B()); }
你可能会争辩说 Dart 在 async
函数中转换 return x;
时,它应该创建一个 Future<T>
,其中 T
是 x
的静态类型而不是从函数的 return 类型推断它。正如 lrn 在评论中解释的那样,这是不可能的,因为你可能有:
class C extends A {}
Future<A> getFuture() async {
await Future.delayed(const Duration(seconds: 1));
if (Random().nextBool()) {
return B();
} else {
return C();
}
}
调用者必须立即得到一个Future
,但直到Future
最终才知道它的值是B
还是C
完成。
I'd have expected it either to fail at compile time like C#
我对 C# 的经验也非常有限,但我认为 C# 会给你一个编译错误,因为 C# 不认为 Generic<SubType>
是 Generic<SuperType>
的子类型,而 Dart 会。