18

I would like to clone a complex object (copy values), not referencing, using Dart 2.

Example:

class Person {
  String name;
  String surname;
  City city;
}

class City {
  String name;
  String state;
}

main List<String> args {
  City c1 = new City()..name = 'Blum'..state = 'SC';
  Person p1 = new Person()..name = 'John'..surname = 'Xuebl'..city = c1;


  Person p2 = // HERE, to clone/copy values... Something similar to p1.clone();
}

What would be the way (best practice) to do this?

Update note: This How can I clone an Object (deep copy) in Dart? was posted some time ago. The focus here is to understand if Dart 2 that is bringing many improvements, has a facility for copying complex objects.

Muka
  • 1,190
  • 1
  • 12
  • 27
  • Possible duplicate of [How can I clone an Object (deep copy) in Dart?](https://stackoverflow.com/questions/13107906/how-can-i-clone-an-object-deep-copy-in-dart) – Maximilian Riegler Jul 16 '18 at 04:36
  • Because the thread was posted some time ago, it was dealing with the Dart 1 version. My intention was to post a new post to specifically address Dart 2, which is bringing excellent improvements, and could have something to do with cloning complex objects. – Muka Jul 16 '18 at 14:30

5 Answers5

10

With the classes you have shown us here, there is nothing shorter than

Person p2 = Person()
  ..name = p1.name
  ..surname = p1.surname
  ..city = (City()..name = p1.city.name..state = p1.city.state);

If you add a clone method to Person and City, then you can obviously use that. There is nothing built in to the language to allow you to copy the state of an object.

I would recommend changing the classes, at least by adding a constructor:

class Person {
  String name;
  String surname;
  City city;
  Person(this.name, this.surname, this.city);
}
class City {
  String name;
  String state;
  City(this.name, this.state);
}

Then you can clone by just writing:

Person P2 = Person(p1.name, p1.surname, City(p1.city.name, p1.city.state));

(And ob-link about names)

I say that there is no language feature to copy objects, but there actually is, if you have access to the dart:isolate library: Sending the object over a isolate communication port. I cannot recommend using that feature, but it's here for completeness:

import "dart:isolate";
Future<T> clone<T>(T object) {
  var c = Completer<T>();
  var port = RawReceivePort();
  port.handler = (Object o) {
    port.close();
    c.complete(o);
  }
  return c.future;
}

Again, I cannot recommend using this approach. It would work for simple objects like this, but it doesn't work for all objects (not all objects can be sent over a communication port, e.g., first-class functions or any object containing a first class function).

Write your classes to support the operations you need on them, that includes copying.

lrn
  • 64,680
  • 7
  • 105
  • 121
  • Understood. I really wanted to avoid doing the copy of attribute values ​​by attribute. In the other alternative, using isolate, it has some impacts as well. Maybe until the release of the final version of Dart 2, we can some more direct way. – Muka Jul 16 '18 at 14:34
  • Dart very much wants to *avoid* having a general way to clone objects without going through a constructor. The only way to safely allow cloning is to require the class itself to expose the functionality. Cloning a class from the outside is fragile and dangerous. – lrn Jul 18 '18 at 09:02
6

My simpler solution just let clone() return a new Person with the current values:

class Person {
  String name;
  String surname;
  City city;
  Person(this.name, this.surname, this.city);
  clone() => Person(name, surname, city);
}

You might further need to recursively clone the objects in your Person. as an example by creating a similar clone() function in the City and using it here as city.clone().
For the strings you will need to check their behavior or also create / add a way for cleaning them.

a.l.e
  • 792
  • 8
  • 16
2

As said, there is no built in solution for that, but if the ideia is to accomplish immutable value types you can check built_value.

https://medium.com/dartlang/darts-built-value-for-immutable-object-models-83e2497922d4

Leo Cavalcante
  • 2,327
  • 2
  • 22
  • 30
1

I noted that using Map.from() do a shallow copy and not a deep copy.

To do a deep copy of a class containing a Map of anoter Class, one solution can be to use a nammed constructor

class MyClassB {
   int myVar;
   
   // Constructor
   MyClassB(this.id);
   
   // Named Constructor to do a deep clone
   MyClassB.clone(MyClassB b){
     id = b.id;
   }
}

class MyClassA { 
  Map<int,MyClassB> mapOfClassB;

  // Constructor
  MyClassA(this.myClassB)

  // Named constructor to do a deep clone
  MyClassA.clone(MyClassA a){
    Map<int,myClassB> m = {};
    myClassB  = a.mapOfClassB.forEach((k,v)=> m[k] = MyClassB.clone(v)); // Use the clone constructor here, if not the maps in MyClassA and MyClassB will be linked
  }
}

main() {
  var b1 = MyClassB(20);
  var a1 = MyClassA({0:b1});
  
  var a2 = MyClass1A.clone(a1);
  
  a2.mapOfClassB[0].id = 50;
  
  print(a1.mapOfClassB[0].id); // Should display 20
  print(a2.(a1.mapOfClassB[0].id) // Should display 50 
}
Adrien Arcuri
  • 1,962
  • 1
  • 16
  • 30
0

Using a package like freezed, you could make deep copies of the complex objects.

Although one downside is that the objects are immutable and you cannot make shallow copies of it. But again, it depends on your use case and how you want your objects to be.

Saurav Panthee
  • 589
  • 5
  • 8