How can I clone an Object (deep copy) in Dart?

86,433

Solution 1

No as far as open issues seems to suggest:

https://github.com/dart-lang/sdk/issues/3367

And specifically:

... Objects have identity, and you can only pass around references to them. There is no implicit copying.

Solution 2

Darts built-in collections use a named constructor called "from" to accomplish this. See this post: Clone a List, Map or Set in Dart

Map mapA = {
    'foo': 'bar'
};
Map mapB = new Map.from(mapA);

Solution 3

Late to the party, but I recently faced this problem and had to do something along the lines of :-

class RandomObject {

  RandomObject(this.x, this.y);

  RandomObject.clone(RandomObject randomObject): this(randomObject.x, randomObject.y);

  int x;
  int y;
}

Then, you can just call copy with the original, like so:

final RandomObject original = RandomObject(1, 2);
final RandomObject copy = RandomObject.clone(original);

Solution 4

I guess for not-too-complex objects, you could use the convert library:

import 'dart:convert';

and then use the JSON encode/decode functionality

Map clonedObject = JSON.decode(JSON.encode(object));

If you're using a custom class as a value in the object to clone, the class either needs to implement a toJson() method or you have to provide a toEncodable function for the JSON.encode method and a reviver method for the decode call.

Solution 5

Unfortunately no language support. What I did is to create an abstract class called Copyable which I can implement in the classes I want to be able to copy:

abstract class Copyable<T> {
  T copy();
  T copyWith();
}

I can then use this as follows, e.g. for a Location object:

class Location implements Copyable<Location> {
  Location({
    required this.longitude,
    required this.latitude,
    required this.timestamp,
  });

  final double longitude;
  final double latitude;
  final DateTime timestamp;

  @override
  Location copy() => Location(
        longitude: longitude,
        latitude: latitude,
        timestamp: timestamp,
      );

  @override
  Location copyWith({
    double? longitude,
    double? latitude,
    DateTime? timestamp,
  }) =>
      Location(
        longitude: longitude ?? this.longitude,
        latitude: latitude ?? this.latitude,
        timestamp: timestamp ?? this.timestamp,
      );
}
Share:
86,433

Related videos on Youtube

george koller
Author by

george koller

Updated on May 07, 2022

Comments

  • george koller
    george koller almost 2 years

    Is there a Language supported way make a full (deep) copy of an Object in Dart?

    Secondary only; are there multiple ways of doing this, and what are the differences?

    Thanks for clarification!

  • george koller
    george koller over 11 years
    Thanks, and I did just end writing a clone method myself for the few classes where I need to make a copy (not just a reference). I was spoiled by Ruby - thought this was a standard language feature.
  • srcspider
    srcspider about 11 years
    it's suppose to be a standard language feature =_="
  • Michał Šrajer
    Michał Šrajer over 9 years
    even in official dart polymer tutorial they implement copyCodelab() function by hand. There really suppose to be such stdlib functionality. I'll file a bug for that.
  • Azael
    Azael about 9 years
    This might work for a simple <String,String> Map, but it will not for more complex maps like <String,Map>. I had the same idea like you, but it didnt work.
  • Moshe Shaham
    Moshe Shaham about 7 years
    from is a constructor. use new Map.from(mapA);
  • stackunderflow
    stackunderflow over 5 years
    @MosheShaham i don't get it
  • stackunderflow
    stackunderflow over 5 years
    how about 6 years later? is this answer still a truth?
  • Luke Pighetti
    Luke Pighetti about 5 years
    @jerinho you are correct that Moshe Shaham's solution does not work for nested maps.
  • Timmmm
    Timmmm about 5 years
    Well this is disappointing. To be fair, many many languages suffer from this issue, even Go has no way to copy an object like you can in C++ or Rust. Anyway here is a recent issue I found for this.
  • Pedro Massango
    Pedro Massango almost 5 years
    Hi @Phill I copy/paste your code and it is not working. Can you improve it?
  • Phill Wiggins
    Phill Wiggins almost 5 years
    Hey @pedromassango. It's difficult to improve that code. Send me your code or implementation and I can show you. Something like:- ```var newObject = Random object.clone(oldObject) will return a copied instance.
  • Pedro Massango
    Pedro Massango almost 5 years
    It was giving me a error, I just replaced super by this
  • Timmmm
    Timmmm almost 5 years
    it loses type information, and it only works for object types that can be represented by JSON. Most importantly it's way slower than it should be.
  • Phill Wiggins
    Phill Wiggins almost 5 years
    Not sure why this was downvoted either. It's the closest and cleanest possible way to clone an object without using reflection.
  • user48956
    user48956 over 4 years
    This is the wrong answer. Just because a language has pass-by-reference calls, doesn't mean deep copying is not supported.
  • JAR.JAR.beans
    JAR.JAR.beans over 4 years
    @user48956 Would be great if you can elaborate. The need is a Map of Maps. Say, a complex configuration, The requirement is to be fully clone this, such that it's protected (and maybe even based on need, changed) inside a specific process, but the original object remains unchanged. many languages supports pass-by-ref but also have deep copying out of the box.
  • user48956
    user48956 over 4 years
    Sure language like scala support deep copies by provided methods to reflect the constructor parameter names, types and values (and you can use them to serialize many classes). Language like python provide reflection methods to find an objects class names and attributes (and use them to perform auto-serialization and deep copying). In 2020, these features from 1990 are what we’ve come to expect in a programmable language.
  • JAR.JAR.beans
    JAR.JAR.beans over 4 years
    Sorry. Lost you at 1990. deepcopy is a convenient method, syntactic sugar. Any one can implement this (some ways for that, from full serialization and deserialization to recursive looping over the object). The question here is, does Dart has this out of the box, The answer is - No. How can this be a wrong answer? Is there a discussion about "is deep copy a good idea", sure. But I don't think it's related.
  • LMD
    LMD over 4 years
    Cloning using JSON is no good idea. It limits the objects you can clone & reduces performance.
  • dfmiller
    dfmiller about 4 years
    Super lame, give-up-the-ship answer. If nothing else you can loop through each of the objects properties to create a new object.
  • K-Dawg
    K-Dawg almost 4 years
    this also doesn't work well if the object has a contains a list of other serializable objects. For example Order has a list of products... even if products are serializable you will see an issue when there's a product in the order.
  • Juliano
    Juliano over 3 years
    The problem of this solution is when you want to set a null value to variable.
  • maPer77
    maPer77 over 3 years
    Really, a suggestion that I can think of now is to set it to null after copying ...: // --------------------------------------- Item item2 = item1.copyWith( id: 2, nome: 'Pedro de Nobrega', lista: ['4', '5', '6', '7', '8'] ); item2.email = null; // ---------------------------------------
  • Juliano
    Juliano over 3 years
    Yeah, but this would not work in a immutable objected, where everything is final. I have a suggestion: Item copyWith({ int Function() id, String Function() name .. }) { return Item( id: id?.call() ?? this.id, nome: nome?.call() ?? this.name, ... ); } Now you can call: item.copyWith(name: () => null);
  • Tuan van Duong
    Tuan van Duong over 3 years
    It works for me when I customise it into ClassName.fromJson(jsonDecode(jsonEncode(object)));. Thanks.
  • Haroon khan
    Haroon khan over 3 years
    In this case, you'll also need to write fromJson() for all the child classes as well which a lot of work to be done.
  • Baig
    Baig over 3 years
    Yes, you can use stackoverflow.com/a/26616081/1556386 solution too if you don't want to implement fromJson()
  • cs guy
    cs guy over 3 years
    seems good but the owner doesnt seem to maintain the repo. I dont suggest using random 3rd party packages like this.
  • LP Square
    LP Square over 3 years
    I personally prefer the copy_with_extension_gen 1.4.0 package, it basically does the same thing but gives you more control and is being maintained
  • Corentin Houdayer
    Corentin Houdayer almost 3 years
    It does not work. Here is the exception on my custom model. Class 'Scenario' has no instance getter 'keys'. Receiver: Instance of 'Scenario' Tried calling: keys
  • Pranav Kasetti
    Pranav Kasetti almost 3 years
    This behaviour comes automatically with type promotion.
  • Edeson Bizerril
    Edeson Bizerril over 2 years
    This is the simplest and most straightforward approach. I tried several measures and they are all too complicated and not worth it.
  • West
    West over 2 years
    This works thanks. Been struggling all day with a bug due to shallow cloning because I always thought Map.from always deep copies and I had been using that.
  • stan
    stan over 2 years
    Great solution, since we have already added JSON support for server communication
  • Adrian Moisa
    Adrian Moisa over 2 years
    Yes, that works great when you already have fromJson() in your models
  • jamesdlin
    jamesdlin about 2 years
    This will copy the Map itself, but it will not perform a deep copy of the Maps keys and values. (Map keys should be immutable objects, though.)
  • adel parsa
    adel parsa about 2 years
    yes, unfortunately, I think this is one of the dart language weaknesses. it hasn't a safe way to simple or deep clone, especially for classes
  • jamesdlin
    jamesdlin about 2 years
    This is not an answer to the question. As asked, the answer to the question is "There is no general way to create a copy of an arbitrary object in Dart". There can be ways to create copies for specific types of objects, but enumerating those would be an overly broad question.
  • jamesdlin
    jamesdlin about 2 years
    Additionally, this is an outdated way to copy Maps. Since at least Dart 2, using Map.from/Set.from/List.from is discouraged since they lose type information. Using Map.of/Set.of/List.of is better, or for Sets and Lists, .toSet()/.toList()`.
  • jamesdlin
    jamesdlin almost 2 years
    Also, I somehow forgot to mention that this does not create a deep copy of a Map. It creates a shallow copy.
  • jamesdlin
    jamesdlin almost 2 years
    @dfmiller No, you can't. The compiler cannot automatically choose which constructor should be invoked for each member, if a constructor is even available. What would the compiler do if an object has a member with declared with the type of an abstract base class? Or, if you expect that "looping over each of the object's properties" be done at runtime, then that would require runtime reflection, which would penalize performance and code size.
  • Andres Castañeda
    Andres Castañeda almost 2 years
    This works for me when i combine it with equatable to do comparisons