Muka
Muka

Reputation: 1200

How to clone (copy values) a complex object in Dart 2

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.

Upvotes: 18

Views: 18463

Answers (5)

Saurav Panthee
Saurav Panthee

Reputation: 659

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.

Upvotes: 0

Adrien Arcuri
Adrien Arcuri

Reputation: 2410

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 
}

Upvotes: 1

a.l.e
a.l.e

Reputation: 868

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.

Upvotes: 6

Leo Cavalcante
Leo Cavalcante

Reputation: 2487

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

Upvotes: 2

lrn
lrn

Reputation: 71693

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.

Upvotes: 10

Related Questions