Dart中List<Parent>运行时类型意外变为List<Child>导致添加子类报错求助
嗨,这个问题我之前也踩过坑,本质是Dart泛型的运行时具体化特性和泛型不变性在搞鬼,给你掰扯清楚:
问题根源
Dart的泛型和Java不一样,它是运行时具体化的——也就是说,列表的真实类型在运行时是保留的,不会被擦除。虽然你把变量items声明成了List<Parent>,但当你把futureFnc.getItems()返回的List<Child>赋值给它时,这个变量在运行时的真实类型还是List<Child>。
而Dart的泛型默认是不变的:List<Child>并不是List<Parent>的子类型,哪怕Child是Parent的子类。所以当你尝试往一个实际是List<Child>的列表里加Sibling对象时,就会触发类型不匹配的错误。
解决办法
给你两个靠谱的解决方案,按需选择:
1. 修正getItems()的返回类型(推荐,如果能改的话)
如果你能控制futureFnc.getItems()的实现,一定要让它真正返回List<Parent>类型的列表,而不是List<Child>。比如在构造列表时明确指定泛型:
Future<List<Parent>> getItems() async { // 用<Parent>明确指定列表的泛型类型 return <Parent>[Child(), Child(), Child()]; }
这样返回的列表运行时类型就是List<Parent>,后续添加Sibling就不会有问题了。
2. 转换列表类型(适用于无法修改getItems的情况)
如果getItems()是第三方库的方法,或者你没法修改它,那就在接收结果时把它转换成真正的List<Parent>:
// 方法一:用List.from创建新列表(最稳妥) items = List.from(await futureFnc.getItems()); // 方法二:用cast做类型转换(注意:如果原列表不可变,可能会有问题) items = (await futureFnc.getItems()).cast<Parent>();
List.from会创建一个全新的List<Parent>实例,彻底摆脱原列表的类型限制;而cast是返回一个类型转换后的视图,性能更好但有一定局限性。
补充小知识
为什么静态类型检查允许你把List<Child>赋值给List<Parent>的变量?这是Dart为了方便开发做的宽松协变检查,但这种宽松只存在于静态检查阶段,运行时还是会严格遵循泛型的不变性,所以才会出现“静态类型没问题,运行时报错”的情况。
备注:内容来源于stack exchange,提问作者Giacomo M

