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

标签 sqlite generics flutter dart casting

我是Flutter和Dart的新手,来自本地Android。

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

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

我像这样设置类(class):

class LiveData<T> {
  ...
}

现在,当我想返回一些数据时,它可以是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;
}

...

问题出在第二块:
cachedData = rawData.map((e) => entity.fromMap(e)).toList() as T;

与错误:
 - Unhandled Exception: type 'List<Entity>' is not a subtype of type 'List<Vehicle>' in type cast

问题就变成了:当我无权访问Entity类时,如何将Vehicle转换为Vehicle。仅将其实例分配给Entity entity变量。

这是一个片段,以演示我对Vehicle的访问:
final Entity entity;

...assign Vehicle instance to entity...

print(entity is Vehicle)  // True

我尝试使用.runtimeType无济于事。我还考虑过将LiveData分为两个类,第二个是LiveDataList。尽管这似乎是不对代码进行错误检查的最简单解决方案,但它可能会打扰我(故意是双关语)并破坏原本非常漂亮的Room端口。



作为一个临时解决方案,我将构建逻辑抽象为一个通用函数,该函数将传递给构造函数中的LiveData
final T Function(List<Map<String, dynamic>> rawData) builder;

现在,我将其命名为cachedData,而不是之前的代码。
// Parse response
cachedData = builder(rawData);

访问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(),
      ...
    );
  }
}

最佳答案

在Dart中(实际上在许多语言中),泛型都带有继承的概念。您会认为,如果Bar继承自Foo,那么该List<Bar>也将可转换为List<Foo>

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

看到问题了吗? MyGenericType_FooMyGenericType_Bar不是彼此的后代。他们是彼此的 sibling ,都从BaseClass扩展。这就是为什么当您尝试将List<Entity>转换为List<Vehicle>时,强制转换不起作用的原因,因为它们是同级类型,而不是父类(super class)型和子类型。

综上所述,虽然不能基于通用类型参数的关系将一种通用类型直接转换为另一种通用类型,但是对于List,有一种方法可以将一种List类型转换为另一种类型:cast方法。

List<Entity> entityList = <Entity>[...];
List<Vehicle> vehicleList = entityList.cast<Vehicle>(); // This cast will work

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

关于sqlite - Dart-使用泛型将List <SuperType>转换为List <SubType>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60461815/

相关文章:

objective-c - objective-c (ios)从远程服务器打开sqlite数据库

objective-c - SQLite 步骤失败 : attempt to write a readonly database , 使用包装器

java - 扩展 ArrayList 以支持事件

ios - 如何使用FCM向iOS发送通知?

Android 的 SQLite 数据库 : Failing to Open a Writeable Instance of a DB Because Could not setLocale()

c - 如何在静态链接的C程序中嵌入luasql.sqlite3?

java - 通配符与通用方法

c# - 我怎样才能使这段代码更通用

firebase - 使用http或database管理firebase实时数据库引用效率更高?

android - Flutter 中的 SharedPreferences 错误