Dart - 使用泛型将 List<SuperType> 转换为 List<SubType>

mic*_*ler 4 sqlite generics casting dart flutter

我是 Flutter 和 Dart 的新手,来自原生 Android。

Android 有一个非常好的数据库抽象架构,称为Room Persistence Library。据我所知,使用 MVVM / MVC 设计模式的 Flutter 不存在这样的数据库抽象架构。

我的解决方案是自己创建一个 Dart 版本。经过一些头痛之后,我几乎完成了它,但我似乎无法LiveData使用泛型正常工作。

我这样设置我的班级:

class LiveData<T> {
  ...
}
Run Code Online (Sandbox Code Playgroud)

现在,当我想返回一些数据时,它可以是ObjectList<Object>。我发现了一个巧妙的技巧来区分两者T

...

// Parse response
// This checks if the type is an instance of a single entity or a list.
if (entity is T) {
  cachedData = rawData.isEmpty ? null : entity.fromMap(rawData.first) as T;
} else {
  cachedData = rawData.map((e) => entity.fromMap(e)).toList() as T;
}

...
Run Code Online (Sandbox Code Playgroud)

问题出在第二块:

cachedData = rawData.map((e) => entity.fromMap(e)).toList() as T;
Run Code Online (Sandbox Code Playgroud)

出现错误:

 - Unhandled Exception: type 'List<Entity>' is not a subtype of type 'List<Vehicle>' in type cast
Run Code Online (Sandbox Code Playgroud)

那么问题就变成了:当我无法访问该类时,如何进行Entity转换。仅将其实例分配给变量。VehicleVehicleEntity entity

这是一个演示我访问的片段Vehicle

final Entity entity;

...assign Vehicle instance to entity...

print(entity is Vehicle)  // True
Run Code Online (Sandbox Code Playgroud)

我尝试过使用.runtimeType没有效果。我也想过分成LiveData两个班,第二个班是LiveDataList。虽然这似乎是不让代码出错的最简单的解决方案,但它会让我烦恼(不好的双关语是故意的)并破坏 Room 的其他相当直接的端口。



作为临时解决方案,我将构建逻辑抽象为一个通用函数,以传递给LiveData构造函数。

final T Function(List<Map<String, dynamic>> rawData) builder;
Run Code Online (Sandbox Code Playgroud)

现在我用它来代替以前的代码来构建cachedData.

// Parse response
cachedData = builder(rawData);
Run Code Online (Sandbox Code Playgroud)

LiveData<List<Vehicle>>访问所有车辆时调用的构造函数Dao<Vehicle>

class VehicleDao implements Dao<Vehicle> {
  ...

  static LiveData<List<Vehicle>> get() {
    return LiveData<List<Vehicle>>(
      ...
      (rawData) => rawData.map((e) => Vehicle.fromMap(e)).toList(),
      ...
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

Abi*_*n47 11

在 Dart 中(实际上在许多语言中),泛型与继承的概念相悖。您可能会认为,如果Bar继承自Foo,那么List<Bar>也可以转换为List<Foo>

由于泛型的工作原理,实际情况并非如此。当您有一个泛型类时,每次将该类与不同类型一起使用时,该类型都会被视为完全独立的类。这是因为当编译器编译这些类型时,class MyGenericType<Foo> extends BaseClassclass MyGenericType<Bar> extends BaseClass基本上会转换为类似class MyGenericType_Foo extends BaseClass和 的类型class MyGenericType_Bar extends BaseClass

你看到问题了吗?MyGenericType_Foo并且MyGenericType_Bar不是彼此的后代。他们是彼此的兄弟姐妹,都来自BaseClass。这就是为什么当您尝试将 a 转换List<Entity>为 时List<Vehicle>,强制转换不起作用,因为它们是同级类型,而不是超类型和子类型。

话虽如此,虽然您不能根据泛型类型参数的关系直接将一种泛型类型转换为另一种泛型类型,但有List一种方法可以将一种类型转换List为另一种类型:cast方法。

List<Entity> entityList = <Entity>[...];
List<Vehicle> vehicleList = entityList.cast<Vehicle>(); // This cast will work
Run Code Online (Sandbox Code Playgroud)

但需要注意的一件事是,如果您从超类型泛型转换为子类型泛型,并且并非列表中的所有元素都是该新类型,则此转换将引发错误。