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

Time:03-04

I tried writing a simple generic method that would iteratively copy a nested List, for example a List<List<int>>. But unfortunately, the recursive call seems to always return List<dynamic>, so I get the following error

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;
}

So if I call

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

T is List<int>

value is T - i.e. List<int>

listDeepCopy(value) should equal listDeepCopy<List<int>>, which would return a List<int>, which should be possible to add to newList, which is a List<List<int>>

Where am I going wrong here, and how can I make something like this work?

CodePudding user response:

I probably would implement it as:

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]]
}

A problem with your approach is that Dart cannot properly infer the generic type parameter for that recursive listDeepCopy(value) call. value is of type T that is known to be a List (which is shorthand for List<dynamic>), and I am not aware of a way to extract the static element type. (Maybe @lrn will see this and provide a better, more complete explanation.)

In such a case, it's better to rely on polymorphism by calling a method on the List that returns a copy of itself: .toList().

(As an example where this matters, consider a shallow copy scenario:

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}');
}

Although both copies preserve the static type of the original List, only copy2 preserves the object's actual (runtime) type. A proper copy depends on the runtime type of the object being copied, and the only robust way to do that is for the object to create a copy of itself.)

CodePudding user response:

The dynamic version will do deep copy but the generic one is no better than list4 = [...list1]; and the dynamic one no better than list5 = jsonDecode(jsonEncode(list1));

import 'dart:convert';

List<T> listDeepCopy<T>(List<T> list) {
  var newList = <T>[];

  for (final value in list) {
    if (value is List<T>) {     
      newList.add(listDeepCopy(value) as T); 
    } else {
      newList.add(value);
    }
  }

  return newList;
}

// Dynamic version
listDeepCopy3(List list) {
  var newList = [];

  for (final value in list) {
    if (value is List) {     
      newList.add(listDeepCopy(value)); 
    } else {
      newList.add(value);
    }
  }
  return newList;
}


void main() {
   List<List<int>> list1 = [
    [1, 2],
    [3, 4]
  ];
  var list2 = listDeepCopy(list1);  
  var list3 = listDeepCopy3(list1);
  var list4 = [...list1]; // shallow copy
  var list5 = jsonDecode(jsonEncode(list1)); // deep copy
  
  list2[0][1] = 9;
  list3[0][1] = 8;
  list4[0][1] = 7;
  list5[0][1] = 6;
  
  print(list1);  
  print(list2); // shallow copy
  print(list3); // deep copy but type is dynamic
  print(list4); // shallow copy
  print(list5); // deep copy but type is dynamic
}
  • Related