Is there a Language supported way to make a full (deep) copy of an Object in Dart?
If multiple options exist, what are their differences?
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);
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.
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);
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.
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,
);
}
To copy an object without reference, the solution I found was similar to the one posted here, however if the object contains MAP or LIST you have to do it this way:
class Item {
int id;
String nome;
String email;
bool logado;
Map mapa;
List lista;
Item({this.id, this.nome, this.email, this.logado, this.mapa, this.lista});
Item copyWith({ int id, String nome, String email, bool logado, Map mapa, List lista }) {
return Item(
id: id ?? this.id,
nome: nome ?? this.nome,
email: email ?? this.email,
logado: logado ?? this.logado,
mapa: mapa ?? Map.from(this.mapa ?? {}),
lista: lista ?? List.from(this.lista ?? []),
);
}
}
Item item1 = Item(
id: 1,
nome: 'João Silva',
email: 'joaosilva#gmail.com',
logado: true,
mapa: {
'chave1': 'valor1',
'chave2': 'valor2',
},
lista: ['1', '2'],
);
// -----------------
// copy and change data
Item item2 = item1.copyWith(
id: 2,
nome: 'Pedro de Nobrega',
lista: ['4', '5', '6', '7', '8']
);
// -----------------
// copy and not change data
Item item3 = item1.copyWith();
// -----------------
// copy and change a specific key of Map or List
Item item4 = item1.copyWith();
item4.mapa['chave2'] = 'valor2New';
See an example on dartpad
https://dartpad.dev/f114ef18700a41a3aa04a4837c13c70e
With reference to #Phill Wiggins's answer, here is an example with .from constructor and named parameters:
class SomeObject{
String parameter1;
String parameter2;
// Normal Constructor
SomeObject({
this.parameter1,
this.parameter2,
});
// .from Constructor for copying
factory SomeObject.from(SomeObject objectA){
return SomeObject(
parameter1: objectA.parameter1,
parameter2: objectA.parameter2,
);
}
}
Then, do this where you want to copy:
SomeObject a = SomeObject(parameter1: "param1", parameter2: "param2");
SomeObject copyOfA = SomeObject.from(a);
Let's say you a have class
Class DailyInfo
{
String xxx;
}
Make a new clone of the class object dailyInfo by
DailyInfo newDailyInfo = new DailyInfo.fromJson(dailyInfo.toJson());
For this to work your class must have implemented
factory DailyInfo.fromJson(Map<String, dynamic> json) => _$DailyInfoFromJson(json);
Map<String, dynamic> toJson() => _$DailyInfoToJson(this);
which can be done by making class serializable using
#JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false)
Class DailyInfo{
String xxx;
}
It only works for object types that can be represented by JSON.
ClassName newObj = ClassName.fromMap(obj.toMap());
or
ClassName newObj = ClassName.fromJson(obj.toJson());
Trying using a Copyable interface provided by Dart.
there is an easier way for this issue
just use ... operator
for example, clone a Map
Map p = {'name' : 'parsa','age' : 27};
Map n = {...p};
also, you can do this for class properties.
in my case, I was needed to clone a listed property of a class.
So:
class P1 {
List<String> names = [some data];
}
/// codes
P1 p = P1();
List<String> clonedList = [...p.names]
// now clonedList is an unreferenced type
There is no built-in way of deep cloning an object - you have to provide the method for it yourself.
I often have a need to encode/decode my classes from JSON, so I usually provide MyClass fromMap(Map) and Map<String, dynamic> toJson() methods. These can be used to create a deep clone by first encoding the object to JSON and then decoding it back.
However, for performance reasons, I usually implement a separate clone method instead. It's a few minutes work, but I find that it is often time well spent.
In the example below, cloneSlow uses the JSON-technique, and cloneFast uses the explicitly implemented clone method. The printouts prove that the clone is really a deep clone, and not just a copy of the reference to a.
import 'dart:convert';
class A{
String a;
A(this.a);
factory A.fromMap(Map map){
return A(
map['a']
);
}
Map<String, dynamic> toJson(){
return {
'a': a
};
}
A cloneSlow(){
return A.fromMap(jsonDecode(jsonEncode(this)));
}
A cloneFast(){
return A(
a
);
}
#override
String toString() => 'A(a: $a)';
}
void main() {
A a = A('a');
A b = a.cloneFast();
b.a = 'b';
print('a: $a b: $b');
}
There's no API for cloning/deep-copying built into Dart.
We have to write clone() methods ourselves & (for better or worse) the Dart authors want it that way.
Deep copy Object /w List
If the Object we're cloning has a List of Objects as a field, we need to List.generate that field and those Objects need their own clone method.
Example of cloning method (copyWith()) on an Order class with a List field of objects (and those nested objects also have a copyWith()):
Order copyWith({
int? id,
Customer? customer,
List<OrderItem>? items,
}) {
return Order(
id: id ?? this.id,
customer: customer ?? this.customer,
//items: items ?? this.items, // this will NOT work, it references
items: items ?? List.generate(this.items.length, (i) => this.items[i].copyWith()),
);
}
Gunter mentions this here.
Note, we cannot use List.from(items) nor [...items]. These both only make shallow copies.
Dart does not share Memory within multiple threads (isolate), so...
extension Clone<T> on T {
/// in Flutter
Future<T> clone() => compute<T, T>((e) => e, this);
/// in Dart
Future<T> clone() async {
final receive = ReceivePort();
receive.sendPort.send(this);
return receive.first.then((e) => e as T).whenComplete(receive.close);
}
}
An example of Deep copy in dart.
void main() {
Person person1 = Person(
id: 1001,
firstName: 'John',
lastName: 'Doe',
email: 'john.doe#email.com',
alive: true);
Person person2 = Person(
id: person1.id,
firstName: person1.firstName,
lastName: person1.lastName,
email: person1.email,
alive: person1.alive);
print('Object: person1');
print('id : ${person1.id}');
print('fName : ${person1.firstName}');
print('lName : ${person1.lastName}');
print('email : ${person1.email}');
print('alive : ${person1.alive}');
print('=hashCode=: ${person1.hashCode}');
print('Object: person2');
print('id : ${person2.id}');
print('fName : ${person2.firstName}');
print('lName : ${person2.lastName}');
print('email : ${person2.email}');
print('alive : ${person2.alive}');
print('=hashCode=: ${person2.hashCode}');
}
class Person {
int id;
String firstName;
String lastName;
String email;
bool alive;
Person({this.id, this.firstName, this.lastName, this.email, this.alive});
}
And the output below.
id : 1001
fName : John
lName : Doe
email : john.doe#email.com
alive : true
=hashCode=: 515186678
Object: person2
id : 1001
fName : John
lName : Doe
email : john.doe#email.com
alive : true
=hashCode=: 686393765
// Hope this work
void main() {
List newList = [{"top": 179.399, "left": 384.5, "bottom": 362.6, "right": 1534.5}, {"top": 384.4, "left": 656.5, "bottom": 574.6, "right": 1264.5}];
List tempList = cloneMyList(newList);
tempList[0]["top"] = 100;
newList[1]["left"] = 300;
print(newList);
print(tempList);
}
List cloneMyList(List originalList) {
List clonedList = new List();
for(Map data in originalList) {
clonedList.add(Map.from(data));
}
return clonedList;
}
This works for me.
Use the fromJson and toJson from your Object's Class on JSON serializing
var copy = ObjectClass.fromJson(OrigObject.toJson());
make a helper class:
class DeepCopy {
static clone(obj) {
var tempObj = {};
for (var key in obj.keys) {
tempObj[key] = obj[key];
}
return tempObj;
}
}
and copy what you want:
List cloneList = [];
if (existList.length > 0) {
for (var element in existList) {
cloneList.add(DeepCopy.clone(element));
}
}
Let's say, you want to deep copy an object Person which has an attribute that is a list of other objects Skills. By convention, we use the copyWith method with optional parameters for deep copy, but you can name it anything you want.
You can do something like this
class Skills {
final String name;
Skills({required this.name});
Skills copyWith({
String? name,
}) {
return Skills(
name: name ?? this.name,
);
}
}
class Person {
final List<Skills> skills;
const Person({required this.skills});
Person copyWith({
List<Skills>? skills,
}) =>
Person(skills: skills ?? this.skills.map((e) => e.copyWith()).toList());
}
Keep in mind that using only this.skills will only copy the reference of the list. So original object and the copied object will point to the same list of skills.
Person copyWith({
List<Skills>? skills,
}) =>
Person(skills: skills ?? this.skills);
If your list is primitive type you can do it like this. Primitive types are automatically copied so you can use this shorter syntax.
class Person {
final List<int> names;
const Person({required this.names});
Person copyWith({
List<int>? names,
}) =>
Person(names: names ?? []...addAll(names));
}
The accepted answer doesn't provide an answer, and the highest-rated answer 'doesn't work' for more complex Map types.
It also doesn't make a deep copy, it makes a shallow copy which seems to be how most people land on this page. My solution also makes a shallow copy.
JSON-cloning, which a few people suggest, just seems like gross overhead for a shallow-clone.
I had this basically
List <Map<String, dynamic>> source = [{'sampledata', []}];
List <Map<String, dynamic>> destination = [];
This worked, but of course, it's not a clone, it's just a reference, but it proved in my real code that the data types of source and destination were compatible (identical in my case, and this case).
destination[0] = source[0];
This did not work
destination[0] = Map.from(source[0]);
This is the easy solution
destionation[0] = Map<String, dynamic>.from(source[0]);
I'm trying to have a base Freezed interface which my app entity interfaces can extend so I can call the freezed functions on the interfaces. I've started the process here which seems to be working so far:
abstract class IUserRegistrationEntity<T> extends FreezedClass<T> {
String get nickName;
String get email;
String get confirmEmail;
String get password;
String get confirmPassword;
}
abstract class FreezedClass<T> {
T get copyWith;
Map<String, dynamic> toJson();
}
freezed class:
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:vepo/domain/user_registration/i_user_registration_entity.dart';
part 'user_registration_entity.freezed.dart';
part 'user_registration_entity.g.dart';
#freezed
abstract class UserRegistrationEntity with _$UserRegistrationEntity {
#Implements.fromString(
'IUserRegistrationEntity<\$UserRegistrationEntityCopyWith<IUserRegistrationEntity>>')
const factory UserRegistrationEntity(
{String nickName,
String email,
String confirmEmail,
String password,
String confirmPassword}) = _IUserRegistrationEntity;
factory UserRegistrationEntity.fromJson(Map<String, dynamic> json) =>
_$UserRegistrationEntityFromJson(json);
}
But now I need to add the fromJson factory constructor to the interface. I think this may be what I'm looking for although I can't really tell how to implement it in my code:
T deserialize<T extends JsonSerializable>(
String json,
T factory(Map<String, dynamic> data),
) {
return factory(jsonDecode(json) as Map<String, dynamic>);
}
You an then call it with:
var myValue = deserialize(jsonString, (x) => MyClass.fromJson(x));
Any help adding the fromJson to my freezed interface would be appreciated.
I've found a way to get the same benefits of programming to an interface or "abstraction" with freezed objects, while still getting to call those freezed functions:
#freezed
abstract class Person with _$Person {
#With(BasicPersonMixin)
const factory Person.basicPerson(
{int? id, String? firstName, String? lastName}) = BasicPerson;
#With(FancyPersonMixin)
const factory Person.fancyPerson({String? firstName, required String extraPropMiddleName, String? lastName}) =
FancyPerson;
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
const Person._();
void functionThatEveryPersonShares() {
print('I am a person');
}
String greet() {
return 'override me with a mixin or abstract class';
}
}
mixin FancyPersonMixin {
String get extraPropMiddleName {
return 'my default middle name is John`;
}
String greet() {
return 'Salutations!';
}
void specialisedFunctionThatOnlyIHave() {
print('My middle name is $extraPropMiddleName');
}
}
mixin BasicPersonMixin {
String greet() {
return 'Hi.';
}
}
Now we have 2 concrete classes: BasicPerson, and FancyPerson which are both a Person. Now we can program to Person throughout the app, and still call .copyWith and .fromJson and so on and so forth. The different types of Person can vary independently from each other by using mixins and still be used as a Person type. Works with generics etc (from docs - #With.fromString('AdministrativeArea<House>')) but I have kept the example simple for this question to most simply show the benefits. You can also make Person extend another base class.
I've found another way to let you be a bit more abstract than my other answer. Say you're in a highly abstract super-class, so you don't want to work with objects as specific as Person. You want to work with "a base freezed object"; just cast your type to dynamic in brackets and go ahead and use copyWith freely. Sure, it's not typesafe, but it's a worthy option if it allows you to do something in a super-class rather than in every sub-class.
mixin LocalSaveMixin<TEntity extends LocalSaveMixin<TEntity>> on Entity {
LocalRepository<TEntity> get $repository;
Ref? get provider;
TEntity $localFetch() {
return ($repository.$localFetch() as dynamic).copyWith(provider: provider)
as TEntity;
}
TEntity $localSave() {
return $repository.$localSave(entity: this as TEntity);
}
}
I need to "fetch" the methods in a Dart Class.
How can I do this?
And I want to be able to call the methods.
May I see an example?
Here's an easy copy-pasteable code sample:
import 'dart:mirrors';
import 'dart:io';
main() {
var im = reflect(new File('test')); // Retrieve the InstanceMirror of some class instance.
im.type.methods.values.forEach((MethodMirror method) => print(method.simpleName));
}
Output is:
existsSync
_delete
exists
directory
_getDecodedLines
readAsTextSync
readAsBytesSync
readAsLinesSync
_directory
throwIfError
lastModifiedSync
readAsLines
open
_ensureFileService
deleteSync
delete
_exists
length
openInputStream
create
_create
readAsText
_openStdioSync
openOutputStream
_fullPath
_lastModified
fullPathSync
readAsBytes
lastModified
_openStdio
_open
openSync
lengthSync
directorySync
fullPath
createSync
_lengthFromName
Here is a basic example:
(Note: You will want to have a (very) up to date version of the SDK for this, this was done in Dart Editor version 0.2.1_r14167, Dart SDK version 0.2.1.2_r14167 Nov 2, 2012)
My most sincere Thanks to Gilad of the Google Dart Team for providing this example!
#import('dart:mirrors');
class MyClass {
String _test;
String get test => _test;
set test(String paramVal) => _test = paramVal;
void my_method() {
}
void print_test(){
print("test string is: ${_test}");
}
MyClass(String test) {
_test = test;
}
}
main() {
MyClass myClass = new MyClass("Make my day, PUNK.");
myClass.print_test();
//ClassMirror myClassMirror = reflect(myClass).type;
InstanceMirror myClassInstanceMirror = reflect(myClass);
ClassMirror MyClassMirror = myClassInstanceMirror.type;
Map<String, MethodMirror> map = MyClassMirror.methods;
print("map = ${map}");
map.values.forEach( (MethodMirror mm){
myClassInstanceMirror.invoke(mm.simpleName,[]);
});
}
Concerning Reflection I have just written a couple of "Helper Functions" for fetching a LIST of the method names (not a Map) and invoking the method... all I can say for sure is that it works right now. There are likely technical reasons for not doing it this way - but in my case this does not run in a complex environment. However, I do think they nicely mask over a lot of details that not everybody is going to want to deal with.
Here's a functioning demonstration with and without the Helper functions:
#import('dart:mirrors');
class MyClass {
String _test;
String get test => _test;
set test(String paramVal) => _test = paramVal;
void my_method1(){print("in method1");}
void my_method2(){print("in method2");}
void print_test(){
print("test string is: ${_test}");
}
MyClass(String test) {
_test = test;
}
}
//Helper Methods
InstanceMirror hMr;
List REFLECT_methods(Object myClass) {hMr=reflect(myClass);return(hMr.type.methods.values);}
REFLECT_invoke(MethodMirror mm){hMr.invoke(mm.simpleName, []);}
main() {
MyClass myClass = new MyClass("Make my day, PUNK.");
print("\n=======Using Raw Reflection================");
InstanceMirror myClassInstanceMirror = reflect(myClass);
ClassMirror MyClassMirror = myClassInstanceMirror.type;
Map<String, MethodMirror> map1 = MyClassMirror.methods;
map1.values.forEach( (MethodMirror mm){
myClassInstanceMirror.invoke(mm.simpleName,[]);
});
print("\n==========Using Helper functions=============");
List list2 = REFLECT_methods(myClass);
list2.forEach((method){
REFLECT_invoke(method);
});
}