文章

Dart泛型深度解析

从泛型类与方法的声明到类型约束与运行时泛型擦除,全面拆解Dart泛型的类型安全实现原理与最佳实践

Dart泛型深度解析

一句话概括

Dart的泛型是一种编译时类型安全机制,通过类型参数化让类、方法和接口能够操作不同类型的数据同时保持类型检查,并且通过类型擦除(Reified Generics)在运行时保留类型信息。

背景与意义

为什么需要泛型?

假设没有泛型,我们要实现一个”保存任意类型数据”的缓存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 没有泛型——只能用 dynamic
class Cache {
  final Map<String, dynamic> _data = {};

  void set(String key, dynamic value) => _data[key] = value;
  dynamic get(String key) => _data[key];
}

void main() {
  final cache = Cache();
  cache.set('name', 'Alice');
  cache.set('age', 25);

  // 灾难:没有类型保护
  String name = cache.get('name'); // 编译通过,运行时正确——碰巧
  int age = cache.get('name');     // ❗ 编译通过!但运行时 age 拿到了 String
  // 这会在后续使用 age.isEven 时抛出 TypeError
}

而有了泛型,编译器就能阻止这种错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 有泛型——类型安全
class Cache<T> {
  final Map<String, T> _data = {};
  void set(String key, T value) => _data[key] = value;
  T get(String key) => _data[key] as T;
}

void main() {
  final stringCache = Cache<String>();
  stringCache.set('name', 'Alice');
  // stringCache.set('age', 25); // ❌ 编译错误:int不能赋值给String

  final intCache = Cache<int>();
  intCache.set('age', 25);
  // intCache.set('name', 'Alice'); // ❌ 编译错误
}

这不仅仅是消除运行时crash——它还带来了IDE智能提示、重构安全性和代码可读性的多重收益。

Dart泛型的独特设计

与其他语言相比,Dart泛型有几个重要特性:

特性JavaTypeScriptC++Dart
类型擦除✅ 编译时擦除✅ 编译时擦除❌ 实例化⚠️ 保留(Reified)
运行时保留List<String>就是List❌ 无运行时类型✅ 每个实例化独立✅ 运行时可知
通配符? extends T / ? super T协变/逆变模板参数无通配符,用约束替代
泛型约束<T extends Bound><T extends Bound>requires<T extends Bound>
泛型函数✅ 模板函数
泛型类型推断✅ 部分✅ 完善❌ C++17部分✅ 完善

概念与定义

泛型术语速览

术语解释示例
类型参数定义时使用的占位符<T>, <E>, <K, V>
类型实参使用时传递的具体类型List<String> 中的 String
有界类型参数限制了类型参数的上界<T extends Comparable>
泛型类带有类型参数的类class Box<T> { T value; }
泛型方法带有类型参数的方法T first<T>(List<T> list) => list[0]
类型推断编译器自动推导类型实参var list = [1, 2, 3] 推断为 List<int>

最小示例:泛型类与泛型方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// generics_demo.dart
import 'dart:math';

// ===== 1. 泛型类 =====
class Result<T> {
  final T? data;
  final String? error;
  final bool isSuccess;

  Result.success(this.data)
      : error = null,
        isSuccess = true;

  Result.failure(this.error)
      : data = null,
        isSuccess = false;

  // 泛型方法
  U fold<U>(U Function(T?) onSuccess, U Function(String) onFailure) {
    if (isSuccess) {
      return onSuccess(data);
    } else {
      return onFailure(error!);
    }
  }

  @override
  String toString() => isSuccess ? 'Result.success($data)' : 'Result.failure($error)';
}

// ===== 2. 多类型参数 =====
class Pair<K, V> {
  final K key;
  final V value;

  Pair(this.key, this.value);

  @override
  String toString() => '($key, $value)';
}

// ===== 3. 有界类型参数 =====
// 类型参数T必须实现Comparable
T maxOf<T extends Comparable<T>>(T a, T b) {
  return a.compareTo(b) >= 0 ? a : b;
}

// 多约束:使用接口组合
abstract class HasArea {
  double get area;
}

abstract class HasName {
  String get name;
}

// 使用extends + & 实现多类型约束(Dart 3.0不直接支持,用接口组合替代)
T describeShape<T extends HasArea & HasName>(T shape) {
  print('${shape.name}的面积: ${shape.area}');
  return shape;
}

// ===== 4. 实际应用:类型安全的仓库模式 =====
abstract class Repository<T> {
  Future<T?> getById(String id);
  Future<List<T>> getAll();
  Future<void> save(T item);
  Future<void> delete(String id);
}

// 内存实现(完整可运行)
class InMemoryRepository<T> implements Repository<T> {
  final Map<String, T> _store = {};

  @override
  Future<T?> getById(String id) async => _store[id];

  @override
  Future<List<T>> getAll() async => _store.values.toList();

  @override
  Future<void> save(T item) async {
    final id = item.hashCode.toString(); // 简化用hashCode作为ID
    _store[id] = item;
  }

  @override
  Future<void> delete(String id) async {
    _store.remove(id);
  }
}

void main() async {
  print('=== 泛型类 ===');
  final success = Result.success(42);
  final failure = Result.failure('网络错误');

  // 模式匹配式的访问
  final message = success.fold(
    (data) => '数据: $data',
    (error) => '错误: $error',
  );
  print(message);

  // 类型安全:编译器知道Result<int>的data是int?
  Result<int> intResult = Result.success(100);
  final length = intResult.fold(
    (data) => data?.isEven ?? false,
    (error) => false,
  );
  print('是否偶数: $length');

  print('\n=== 多类型参数 ===');
  final pair = Pair<String, int>('age', 25);
  print(pair);

  print('\n=== 类型约束 ===');
  print('max(3, 7) = ${maxOf(3, 7)}');
  print('max("apple", "banana") = ${maxOf("apple", "banana")}');
  // maxOf(42, "hello"); // ❌ 编译错误:类型不匹配

  print('\n=== 泛型仓库 ===');
  final repo = InMemoryRepository<String>();
  await repo.save('Hello Dart');
  await repo.save('泛型真好用');
  final items = await repo.getAll();
  for (final item in items) {
    print('  - $item');
  }
}

核心知识点拆解

1. 泛型类的声明与使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 基本语法
class Box<T> {
  T content;

  Box(this.content);

  T getContent() => content;
  void setContent(T newContent) {
    content = newContent;
  }
}

// 使用方式
final stringBox = Box<String>('hello');
// final stringBox = Box('hello'); // 类型推断也可
final intBox = Box<int>(42);

// String box只能放String
// stringBox.setContent(42); // ❌ 编译错误

// 多层泛型
class Container<T> {
  final List<T> items;
  Container(this.items);
}

final container = Container([Box('a'), Box('b')]);
// container的类型是 Container<Box<String>>

2. 泛型方法的声明与类型推断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 普通类中的泛型方法
class Utils {
  // 泛型方法:类型参数在方法名之前
  static T? firstOrNull<T>(List<T> items) {
    return items.isEmpty ? null : items.first;
  }

  // 泛型方法的类型推断依赖于参数类型
  static List<T> filter<T>(List<T> items, bool Function(T) predicate) {
    return items.where(predicate).toList();
  }
}

void main() {
  // 显式指定类型参数
  final first = Utils.firstOrNull<int>([1, 2, 3]);

  // 类型推断:编译器从参数推导出 T = String
  final filtered = Utils.filter(
    ['apple', 'banana', 'cherry'],
    (s) => s.startsWith('b'), // (s)的类型是String,自然推断T = String
  );
  print(filtered); // [banana]

  // 泛型方法的参数类型还可以从上下文推断
  List<String> result = Utils.filter(
    ['a', 'bb', 'ccc'],
    (s) => s.length > 1,
  ); // T自动为String,因为返回值是List<String>
}

3. 类型约束(Bounded Type Parameters)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 单约束
class SortedList<T extends Comparable<T>> {
  final List<T> _items = [];

  void add(T item) {
    _items.add(item);
    _items.sort((a, b) => a.compareTo(b)); // 保证T有compareTo方法
  }

  List<T> get items => List.unmodifiable(_items);
}

void main() {
  final numbers = SortedList<int>();
  numbers.add(3);
  numbers.add(1);
  numbers.add(2);
  print(numbers.items); // [1, 2, 3]

  // SortedList<Object>(); // ❌ Object没有实现Comparable
}

// 使用抽象类+泛型约束实现功能类似multi-bound
abstract class Identifiable {
  String get id;
}

abstract class Cacheable {
  bool get needsRefresh;
}

// 通过泛型约束"模拟"多约束
class ManagedItem<T extends Identifiable & Cacheable> {
  // Dart不支持直接 & 语法,这里展示设计意图
  // 实际需要让T同时实现两个接口
}

// 正确方式:让具体的类实现多个接口
class ApiResource implements Identifiable, Cacheable {
  @override
  final String id;
  @override
  final bool needsRefresh;

  ApiResource(this.id, this.needsRefresh);
}

4. 泛型与集合

Dart的集合库是泛型最典型的应用场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void main() {
  // List
  final names = <String>['Alice', 'Bob'];
  // names.add(42); // ❌ 编译错误

  // Set
  final uniqueIds = <int>{1, 2, 3, 3, 2}; // {1, 2, 3}

  // Map
  final scores = <String, int>{
    'Alice': 95,
    'Bob': 87,
  };

  // 集合的协变:List<Object>可以接收List<String>的引用
  // 但要注意这不是类型安全的(在Dart中需要显式转换)
  List<Object> objects = <String>['a', 'b']; // ✅ 协变

  // 不可变集合
  final readOnly = List<String>.unmodifiable(['x', 'y']);

  // 集合的泛型方法
  final numbers = [1, 2, 3, 4, 5];
  final even = numbers.where((n) => n.isEven).toList();
  // even 的类型是 List<int>(编译器正确推断)
}

5. 泛型的运行时行为(Reified Generics)

Dart的一个关键特性是在运行时保留泛型信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void main() {
  final list = <int>[1, 2, 3];

  // Java中 list instanceof List<Integer> 是不合法的
  // 但在Dart中:
  print(list is List<int>);     // true ✅
  print(list is List<String>);  // false ✅ 在运行时也知道类型不同
  print(list.runtimeType);      // List<int>

  // 类型擦除语言做不到的事情
  // Java: List<String> list = ...; list instanceof List → true; 但不知道<String>
  // Dart: 运行时也能感知到具体的类型参数

  // 泛型类型的运行时检查
  void checkType<T>(T value) {
    print('值的类型: ${value.runtimeType}');
    print('T的类型: $T'); // 在Dart中T作为Type可用
  }

  checkType<String>('hello');   // 输出: T的类型: String
  checkType<int>(42);           // 输出: T的类型: int

  // 类型字面量比较
  assert(List<int> == List<int>); // true
}

Reified vs Erased 对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Dart (Reified):
void main() {
  var list = <int>[1, 2, 3];
  print(list.runtimeType); // List<int>
  if (list is List<int>) { // 可以在运行时区分
    print('是整数列表');
  }
}

// Java (Erased) —— 概念对比:
// List<Integer> list = new ArrayList<>();
// System.out.println(list.getClass());  // ArrayList.class —— 擦除了<Integer>
// if (list instanceof ArrayList<Integer>) {} // 编译错误!无法运行时检查泛型类型

实战案例:泛型驱动的数据管线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// data_pipeline.dart - 使用泛型构建类型安全的数据处理管线

// 1. 管线阶段定义
abstract class PipelineStage<I, O> {
  const PipelineStage();
  Future<O> process(I input);
}

// 2. 具体阶段实现
class ValidateStage extends PipelineStage<String, String> {
  @override
  Future<String> process(String input) async {
    if (input.isEmpty) throw ArgumentError('输入不能为空');
    if (input.length > 100) throw ArgumentError('输入超长');
    print('  [验证] 通过: "$input"');
    return input;
  }
}

class TransformStage extends PipelineStage<String, Map<String, dynamic>> {
  @override
  Future<Map<String, dynamic>> process(String input) async {
    // 解析JSON字符串
    final parts = input.split('|');
    final result = <String, dynamic>{
      'name': parts[0].trim(),
      'age': int.tryParse(parts[1].trim()) ?? 0,
      'email': parts.length > 2 ? parts[2].trim() : '',
    };
    print('  [转换] 结果: $result');
    return result;
  }
}

class EnrichStage extends PipelineStage<Map<String, dynamic>, Map<String, dynamic>> {
  @override
  Future<Map<String, dynamic>> process(Map<String, dynamic> input) async {
    // 增加额外信息
    input['processedAt'] = DateTime.now().toIso8601String();
    input['version'] = 2;
    print('  [丰富] 已增加元数据');
    return input;
  }
}

class PersistStage extends PipelineStage<Map<String, dynamic>, int> {
  final List<Map<String, dynamic>> _storage = [];

  @override
  Future<int> process(Map<String, dynamic> input) async {
    _storage.add(input);
    final id = _storage.length - 1;
    print('  [持久化] 存储 ID=$id');
    return id;
  }
}

// 3. 管线组合器——链式调用
class Pipeline {
  // 使用泛型追踪管线类型状态
  static PipelineStage<S, E> combine<S, I, E>(
    PipelineStage<S, I> first,
    PipelineStage<I, E> second,
  ) {
    return _CombinedStage(first, second);
  }
}

class _CombinedStage<S, I, O> extends PipelineStage<S, O> {
  final PipelineStage<S, I> _first;
  final PipelineStage<I, O> _second;

  const _CombinedStage(this._first, this._second);

  @override
  Future<O> process(S input) async {
    final intermediate = await _first.process(input);
    return await _second.process(intermediate);
  }
}

// 4. 泛型管线运行器
class PipelineRunner {
  // 使用递归泛型约束确保管线类型的一致性
  static Future<O> run<I, O>(
    PipelineStage<I, O> stage,
    I input,
  ) async {
    print('管线开始: ${I}${O}');
    final result = await stage.process(input);
    print('管线完成: $result');
    return result;
  }

  // 多阶段管线:使用链式组合
  static PipelineStage<I, O> chain<I, M, O>(
    PipelineStage<I, M> first,
    PipelineStage<M, O> second,
  ) {
    return _CombinedStage<I, M, O>(first, second);
  }
}

// 5. 使用示例
void main() async {
  print('===== 数据管线演示 =====');

  // 构建管线:String → String(验证) → Map(转换) → Map(丰富) → int(存储)
  final pipeline = PipelineRunner.chain(
    PipelineRunner.chain(
      PipelineRunner.chain(
        ValidateStage(),
        TransformStage(),
      ),
      EnrichStage(),
    ),
    PersistStage(),
  );

  // 运行管线
  try {
    final resultId = await pipeline.process('张三|28|zhang@example.com');
    print('最终结果: 存储ID=$resultId');
  } catch (e) {
    print('管线异常: $e');
  }

  // 验证错误场景
  print('\n===== 验证失败场景 =====');
  try {
    final validateOnly = PipelineRunner.chain(
      ValidateStage(),
      TransformStage(),
    );
    await validateOnly.process(''); // 空输入触发验证错误
  } catch (e) {
    print('预期的错误: $e');
  }
}

底层原理(源码分析)

Dart VM中泛型的表示

Dart VM中的泛型通过类型参数对象(TypeArguments对象)在运行时实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Dart VM内部表示(伪代码)
class Instance {
  // 每个对象包含指向其类型的引用
  Class cls;
  // 对于泛型类型,还包含类型实参
  TypeArguments? typeArgs;
}

// List<String> 在VM内部表示为:
// Instance {
//   cls: List,
//   typeArgs: [StringClass]
// }

// 这就是为什么 is List<String> 能在运行时正确工作
// ——VM会检查typeArgs是否匹配

类型推断的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Dart分析器的类型推断核心逻辑(简化)
class TypeInferrer {
  Type inferGenericInvocation(GenericInvocation invocation) {
    // 1. 检查是否有显式的类型参数
    if (invocation.typeArguments != null) {
      return createParameterizedType(
        invocation.callee,
        invocation.typeArguments,
      );
    }

    // 2. 从参数推断
    final inferredArgs = <Type>[];
    for (var i = 0; i < invocation.typeParameters.length; i++) {
      final param = invocation.typeParameters[i];
      final arg = invocation.arguments[i];
      // 从参数表达式的类型推断
      inferredArgs.add(inferFromContext(arg, param.bound));
    }

    // 3. 从返回类型上下文推断
    if (invocation.expectedReturnType != null) {
      // 进一步细化推断
    }

    return createParameterizedType(invocation.callee, inferredArgs);
  }
}

协变与类型安全

Dart在集合上使用协变(Covariance),这意味着:

1
2
3
4
5
6
7
8
9
10
11
void main() {
  // 协变:List<Cat> 可以被当作 List<Animal>
  List<Cat> cats = [Cat('Tom')];
  List<Animal> animals = cats; // ✅ 协变

  // 但这不是完全类型安全的
  animals.add(Dog('Rex')); // ✅ 编译通过(Dog也是Animal)
  // 但现在cats实际上包含了Dog!——运行时不报错
  final tom = cats[0]; // 这是Cat
  final rex = cats[1]; // 这是Dog——类型!🐱🐶
}
1
2
3
4
5
6
7
8
协变的安全性问题:
看似类型安全,实际可能破坏:
List<Cat> → 能add(Dog)(因为Dog is Animal)

Dart的设计选择:运行时允许ListContent变化,因为:
1. 大多数情况下开发者不会在List<Cat>中add(Dog)
2. 在read-only场景中协变非常有用
3. List在Dart中被视为可变的(Mutable)

安全的方案:使用不可变集合:

1
2
3
4
5
void main() {
  final cats = List<Cat>.unmodifiable([Cat('Tom')]);
  // cats.add(Dog()); // ❌ 编译错误——unmodifiable
  List<Animal> animals = cats; // ✅ 安全协变
}

高频面试题解析

问题1:Dart的泛型是Reified的,这意味着什么?相比Java的Type Erasure有什么优势?

解析

Reified(具体化) 意味着泛型类型参数在运行时不会擦除——Dart VM在运行时知道 List<int>List<String> 是不同类型。

能力Dart (Reified)Java (Erased)
is List<int> 检查✅ 运行时正确判断❌ 编译错误,不能做
list.runtimeTypeList<int>ArrayList(擦除了泛型)
重载根据泛型区分的函数✅ 可以❌ 擦除后方法签名相同
创建泛型数组List<int>.filled(3, 0)new T[10] 不合法
反射获取类型参数✅ 运行时仍可知❌ 参数信息被擦除

实际收益

1
2
3
4
5
6
7
8
9
10
11
// Dart中可以用泛型做JSON反序列化的类型保护
T fromJson<T>(Map<String, dynamic> json) {
  if (T == User) {
    return User.fromJson(json) as T;
  } else if (T == Product) {
    return Product.fromJson(json) as T;
  }
  throw ArgumentError('Unknown type: $T');
}
// 调用时自动推断:User user = fromJson<User>(jsonData);
// 这种模式在Java中需要传入Class<T>参数!

问题2:Dart中 extendsimplementsmixin 在泛型约束中分别如何使用?

解析

泛型约束中只有 extends 关键字,但可以通过组合实现多种约束:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 1. 单类约束:T必须是某个类的子类
abstract class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

class Kennel<T extends Animal> {
  final T pet;
  Kennel(this.pet);
}
// Kennel<Dog> ✅
// Kennel<String> ❌ String不是Animal的子类

// 2. 单接口约束:T必须实现某个接口
mixin Flyable {
  void fly();
}
class Bird with Flyable { void fly() => print('飞'); }
class Plane with Flyable { void fly() => print('飞'); }

class AirTraffic<T extends Flyable> {
  void control(T vehicle) => vehicle.fly();
}
// AirTraffic<Bird> ✅
// AirTraffic<Dog> ❌ Dog没有实现Flyable

// 3. 组合约束的实现方式
abstract class HasId {
  String get id;
}
abstract class HasName {
  String get name;
}
// 方法一:让类型同时实现两个接口
class User implements HasId, HasName {
  @override final String id;
  @override final String name;
  User(this.id, this.name);
}

// 方法二:定义组合接口
abstract class IdentifiableNamed implements HasId, HasName {}

// 方法三:使用未来可能的 & 语法(Dart 3.0路线图中)
// class Registry<T extends HasId & HasName> { /* ... */ }
// 目前需要在使用时确保具体类实现了所有接口

一句话总结:Dart泛型约束只用 extends,但通过接口组合(implement多个接口)来模拟多约束。

问题3:Dart泛型方法中类型参数是从哪里推断出来的?推断规则是什么?

解析

Dart的泛型类型推断不依赖调用处的返回类型赋值,而是从参数类型赋值上下文共同推断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 推断来源1:实际参数的类型
T identity<T>(T value) => value;

void main() {
  var x = identity(42);            // T = int(从参数42推断)
  var y = identity('hello');       // T = String(从参数"hello"推断)

  // 推断来源2:赋值目标
  List<String> list = identity(['a', 'b']); // 上下文要求List<String>
  // 这里identity的参数是List<String>(因为['a','b']是List<String>)
  // 所以 T = List<String>

  // 推断来源3:方法链上下文
  final numbers = [1, 2, 3, 4, 5];
  final doubled = numbers.map((n) => n * 2).toList();
  // map的参数是 int Function(int),但不是直接从赋值推断
  // 而是从numbers是List<int>推断出map的T=int
  // 继而toList<Int>()自动匹配

  // 推断失败时
  // var ambiguous; // 没有初始化器 → dynamic
  // identity(ambiguous); // T = dynamic
}

推断优先级

1
1. 显式类型参数 > 2. 实际参数类型 > 3. 返回类型上下文

总结与扩展

核心要点

  1. 编译时安全:泛型让不匹配的类型在编译时被捕获,而非运行时crash
  2. Reified泛型:Dart在运行时保留类型信息,is List<int> 检查可以有效工作
  3. 类型约束:通过 T extends Bound 限制类型参数可接受的类型范围
  4. 协变设计List<Cat> 可赋值给 List<Animal>,带来了灵活但也引入了运行时类型风险
  5. 类型推断:Dart能从参数类型和上下文中自动推断类型参数

扩展思考

  • 泛型与Flutter WidgetStatefulWidget 的泛型参数 T extends StatefulWidgetState 能安全访问 widget 属性
  • 协变与不可变性:Flutter推崇不可变性,大量使用 finalconst,这恰好缓解了协变的风险问题
  • Future与泛型Future<T> 是Dart中最常用的泛型类型之一,理解泛型是理解异步编程的基础
  • 模式匹配与泛型:Dart 3.0的 switch + sealed class + 泛型的组合,可以写出既类型安全又简洁的代数数据类型代码
本文由作者按照 CC BY 4.0 进行授权