avatar
Published on

Flutter Dart 深拷贝全方案:Map 与实体类全解析

Flutter Dart 深拷贝全方案:Map 与实体类全解析

Dart 深拷贝全方案:Map 与实体类全解析

在 Web 开发中,JSON.parse(JSON.stringify(obj)) 是实现深拷贝的常用方案(函数等特殊类型除外)。而在 Dart 中,深拷贝场景同样高频,尤其涉及 Map 和实体类时。Dart 没有内置通用深拷贝 API,本文结合 Dart 3.x 特性,整理一套实用方案,覆盖主流场景。

一、Map 集合的深拷贝

Map 深拷贝需区分基础类型嵌套复杂类型,适配不同方案:

1. 基础方案:JSON 序列化拷贝

最贴近 Web 开发思路,通过 jsonEncode 转字符串再用 jsonDecode 解析,适合简单 Map:

import 'dart:convert';

void main() {
  Map<String, dynamic> originalMap = {
    'name': '张三',
    'age': 25,
    'hobbies': ['篮球', '编程']
  };

  // 深拷贝
  Map<String, dynamic> clonedMap = jsonDecode(jsonEncode(originalMap)) as Map<String, dynamic>;

  // 验证独立性
  clonedMap['age'] = 30;
  clonedMap['hobbies'].add('阅读');
  print('原始:$originalMap 拷贝后:$clonedMap');
}

优缺点

  • 优点:代码简洁,无依赖,适合简单结构;

  • 缺点:不支持函数、DateTime 等特殊类型(会丢失类型信息)。

2. 手动遍历拷贝(复杂嵌套场景)

当 Map 嵌套自定义对象时,手动遍历逐层拷贝更安全:

class Person {
  String name;
  int age;
  Person(this.name, this.age);
  // 自定义拷贝方法
  Person.copy(Person other) : name = other.name, age = other.age;
  
  String toString() => 'Person($name, $age)';
}

void main() {
  Map<String, Person> personMap = {
    'user1': Person('李四', 28),
    'user2': Person('王五', 32)
  };

  // 深拷贝
  Map<String, Person> clonedMap = personMap.map((k, v) => MapEntry(k, Person.copy(v)));
  
  clonedMap['user1']?.name = '赵六';
  print('原始:$personMap 拷贝后:$clonedMap');
}

3. 第三方工具:deepcopy 包

多层嵌套集合(Map/List/Set 混合)推荐用 deepcopy 包,支持递归拷贝:

  1. 安装依赖:

dependencies:
  deepcopy: ^1.1.2
  1. 使用示例:

import 'package:deepcopy/deepcopy.dart';

void main() {
  Map<String, dynamic> complexMap = {
    'user': {'name': '孙七', 'age': 26},
    'friends': [{'name': '周八'}, {'name': '吴九'}]
  };

  Map<String, dynamic> clonedMap = complexMap.deepCopy();
  clonedMap['user']['name'] = '郑十';
  print('原始:$complexMap 拷贝后:$clonedMap');
}

二、实体类的深拷贝

自定义实体类是 Flutter 开发核心,深拷贝需兼顾简洁性和嵌套场景:

1. 基础方案:JSON 序列化转实体

通过 fromJsontoJson 配合 JSON 序列化,适合简单实体:

import 'dart:convert';

class ApiResponse {
  final int code;
  final String? message;

  ApiResponse({required this.code, this.message});
  // 从JSON构建
  ApiResponse.fromJson(Map<String, dynamic> json)
      : code = json['code'] as int,
        message = json['message'] as String?;
  // 转为JSON
  Map<String, dynamic> toJson() => {'code': code, 'message': message};
}

void main() {
  final original = ApiResponse(code: 200, message: '成功');
  // 深拷贝
  final cloned = ApiResponse.fromJson(original.toJson());
}

缺点:嵌套实体需逐层处理,不支持 DateTime 等特殊类型。

2. 进阶方案:自定义拷贝构造函数

复杂嵌套实体推荐用拷贝构造函数,精准控制拷贝逻辑:

class Address {
  String city;
  Address({required this.city});
  // 拷贝构造函数
  Address.copy(Address other) : city = other.city;
}

class User {
  String name;
  Address address;
  User({required this.name, required this.address});
  // 嵌套拷贝
  User.copy(User other) : name = other.name, address = Address.copy(other.address);
}

void main() {
  final original = User(name: '钱十一', address: Address(city: '深圳'));
  final cloned = User.copy(original);
  cloned.address.city = '广州'; // 不影响原始对象
}

3. 高效方案:注解工具(如 dart_mappable)

大型项目推荐用注解工具自动生成代码,支持嵌套拷贝:

  1. 安装依赖:

dependencies:
  dart_mappable: ^4.2.2
dev_dependencies:
  build_runner: ^2.4.0
  dart_mappable_builder: ^4.2.2
  1. 定义实体并生成代码:

()
class Product with ProductMappable {
  final String name;
  final double price;
  Product({required this.name, required this.price});
}
// 执行命令生成代码:dart run build_runner build
  1. 使用示例:

void main() {
  final original = Product(name: '教程', price: 99.0);
  // 拷贝并修改
  final cloned = original.copyWith(name: '深拷贝教程');
}

三、方案选型建议

方案适用场景核心优势
JSON 序列化简单 Map/实体类简洁无依赖
手动拷贝构造函数复杂嵌套实体类类型安全,控制精准
deepcopy 包复杂嵌套集合一键递归拷贝
dart_mappable 注解大型项目、多嵌套实体自动生成代码,支持链式拷贝

四、注意事项

  1. 空安全:Dart 3.x 强制空安全,拷贝时需处理可选参数;
  2. 特殊类型DateTime 等需手动转换(如序列化时转时间戳);
  3. 性能:JSON 序列化在大数据量场景性能较差,优先选注解工具或手动拷贝。