returns List<T> 代替 returns List<dynamic> 的通用方法

Generic method that returns List<T> instead returns List<dynamic>

我尝试编写一个简单的通用方法来迭代复制嵌套的 List,例如 List<List<int>>。但不幸的是,递归调用似乎总是return List<dynamic>,所以我得到以下错误

The argument type List<dynamic> can't be assigned to the parameter type T
List<T> listDeepCopy<T>(List<T> list){
  List<T> newList = List<T>();

  list.forEach((value) {
    if( value is List ){
      newList.add(listDeepCopy(value));  // <-- listDeepCopy() always returns List<dynamic>
    }
    else{
      newList.add(value);      
    }
  });

  return newList;
}

所以如果我打电话

List<List<int>> list = [[1,2],[3,4]];
List<List<int>> copy = listDeepCopy(list);

TList<int>

valueT - 即 List<int>

listDeepCopy(value) 应该等于 listDeepCopy<List<int>>,这将 return 一个 List<int>,它应该可以添加到 newList,这是一个 List<List<int>>

我哪里出错了,我怎样才能使这样的东西起作用?

我可能会将其实现为:

List<T> listDeepCopy<T>(List<T> list) {
  var copy = list.toList();
  for (var i = 0; i < copy.length; i += 1) {
    var element = copy[i];
    if (element is List) {
      copy[i] = listDeepCopy(element) as T;
    }
  }
  return copy;
}

void main() {
  List<List<int>> list = [
    [1, 2],
    [3, 4]
  ];
  List<List<int>> copy = listDeepCopy(list);

  list[0][0] = 99;
  print(copy); // Prints: [[1, 2], [3, 4]]
}

您的方法存在一个问题,即 Dart 无法正确推断递归 listDeepCopy(value) 调用的泛型类型参数。 value 属于 T 类型,已知是 List(对于 List<dynamic> 是 shorthand),我不知道提取方法静态元素类型。 (也许@lrn 会看到并提供更好、更完整的解释。)

在这种情况下,最好通过调用 List 上的方法来依赖多态性,returns 是其自身的副本:.toList().

(作为一个重要的例子,考虑一个浅拷贝场景:

List<T> shallowCopy1<T>(List<T> list) => <T>[...list];

List<T> shallowCopy2<T>(List<T> list) => list.toList();

extension StaticType<T> on T {
  Type get staticType => T;
}

void main() {
  List<num> list = <int>[1, 2, 3];
  var copy1 = shallowCopy1(list);
  var copy2 = shallowCopy2(list);

  print('original: staticType: ${list.staticType}, runtimeType: ${list.runtimeType}');
  print('copy1: staticType: ${copy1.staticType}, runtimeType: ${copy1.runtimeType}');
  print('copy2: staticType: ${copy2.staticType}, runtimeType: ${copy2.runtimeType}');
}

尽管两个副本都保留了原始 Liststatic 类型,但只有 copy2 保留了对象的实际(运行时)类型。正确的复制取决于被复制对象的 运行时 类型,唯一可靠的方法是让对象创建自己的副本。)

你不能按照你想要的方式去做。

问题是 deepClone<T>List<dynamic> 转换为 List<T>(这很好),然后尝试将本身是列表的元素转换为类型化列表...但是你不知道类型。 实际上,当您检查 value is List 时,您不知道要将其转换为哪种列表。 有两种情况:

  • 对于某些类型 XTList<X>Iterable<X>,但您无法 获取您的动手 X。 Dart 不允许您在运行时解构类型。
  • TObject 或另一种内部没有“列表元素”类型的通用超类型,然后您根本没有关于要转换的 List 类型的任何信息嵌套列表到。 (这实际上是最简单的情况,因为那样的话你根本就不应该 deepClone 列表)。

有一种方法可以确定您处于哪种情况 (<T>[] is List<Iterable<Object?>>),但它对前一种情况没有帮助,除非您想对所有可能的类型进行详尽搜索X 可能是。

我要做的是构建一个转换器,而不是使用单个函数。

abstract class Cloner<T> {
  const factory Cloner() = _ValueCloner<T>;
  T clone(dynamic source);
  Cloner<List<T>> get list => _ListCloner(this);
}
abstract class _BaseCloner<T> implements Cloner<T> {
  const _BaseCloner();
  Cloner<List<T>> get list => _ListCloner<T>(this);
}
class _ValueCloner<T> extends _BaseCloner<T> {
  const _ValueCloner();
  T clone(dynamic source) => source as T;
}
class _ListCloner<T> extends _BaseCloner<List<T>> {
  final Cloner<T> _base;
  _ListCloner(this._base);
  List<T> clone(dynamic source) =>
     <T>[for (var o in source as List<dynamic>) _base.clone(o)];
}

然后,如果您确实知道数据的类型,您可以将克隆器构建为:

var typedList =
    Cloner<int>().list.list.clone(
        <dynamic>[<dynamic>[1, 2], <dynamic>[3, 4]]);

会产生一个 List<List<int>>,值为 <List<int>>[<int>[1, 2], <int>[3, 4]]