Дарт - Список Кастинга <SuperType>в Список <SubType>с использованием дженериков - PullRequest
0 голосов
/ 29 февраля 2020

Я новичок во Флаттере и Дарт, родом из Android.

Android имеет очень хорошую архитектуру абстракции базы данных, которая называется Room Persistence Library . Насколько мне известно, такой архитектуры абстракции базы данных для Flutter не существует, используя шаблоны проектирования MVVM / MVC.

Мое решение состояло в том, чтобы самому создать его версию Dart. Я сделал это в значительной степени после нескольких головных болей, но я не могу заставить LiveData правильно работать с использованием дженериков.

Я настроил свой класс так:

class LiveData<T> {
  ...
}

Сейчас когда я хочу вернуть некоторые данные, это может быть Object или List<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.



В качестве временного решения , Я абстрагировал логи сборки c в обобщенную функцию c для передачи 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(),
      ...
    );
  }
}

1 Ответ

0 голосов
/ 29 февраля 2020

В дартсе (да и вообще во многих языках) винты дженерики с концепцией наследования. Вы могли бы подумать, что если Bar наследуется от Foo, то List<Bar> также будет преобразовываться в List<Foo>.

На самом деле это не так из-за того, как работают дженерики. Когда у вас есть обобщенный класс c, каждый раз, когда вы используете этот класс с другим типом, этот тип обрабатывается как совершенно отдельный класс. Это потому, что когда компилятор компилирует эти типы, class MyGenericType<Foo> extends BaseClass и class MyGenericType<Bar> extends BaseClass в основном преобразуются во что-то вроде class MyGenericType_Foo extends BaseClass и class MyGenericType_Bar extends BaseClass.

Вы видите проблему? MyGenericType_Foo и MyGenericType_Bar не являются потомками друг друга. Они братья и сестры друг от друга, оба простираются от BaseClass. Вот почему, когда вы пытаетесь преобразовать List<Entity> в List<Vehicle>, приведение не работает, потому что они являются типами-братьями, а не супертипами и подтипами.

При всем этом говорится, пока вы не можете напрямую приведите один тип generi c к другому на основе отношения параметра типа generi c, в случае List существует способ преобразования одного типа List в другой: метод cast.

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

Одна вещь, на которую следует обратить внимание, если вы преобразуете из обобщенного типа общего типа c в обобщенный тип общего значения c, и не все элементы списка являются этим новым типом, это приведение выдаст ошибку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...