Dart: Dart 泛型 — 类型安全与可复用编程
泛型是代码的模板 — 一次编写,多种类型复用,编译器帮你把关类型安全。
1. 你将学到
- 泛型类与泛型函数的定义
- 泛型约束:extends 与多约束
- 泛型与集合的协作
- 协变与逆变(type bounds)
- Bob 场景:DataPipeline 中的泛型数据转换器
DataTransformer<T, R>
2. 一个开发者的真实故事
(1) 痛点:重复代码导致维护噩梦
Charlie 在 DataPipeline 中为每种数据类型写了单独的解析器:OrderParser、ProductParser、CustomerParser。三个解析器的逻辑几乎一样(读取 → 验证 → 转换),只是输入输出类型不同。每次修改解析逻辑,都要改 3 处,一次漏改导致 Product 的解析没有应用新的验证规则。
(2) 泛型的解法
用泛型类 DataTransformer<T, R> 统一三种解析器,类型参数让编译器保证类型安全,代码只需维护一份。
abstract class DataTransformer<T, R> {
R transform(T input);
List``<R>`` batch(List``<T>`` inputs) => inputs.map(transform).toList();
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
(3) 收益
- 3 个解析器合并为 1 个泛型类,代码量减少 70%
- 修改验证逻辑只需改一处,所有类型自动生效
- 编译器保证类型安全,不会把 Order 当 Product
3. 泛型基础
(1) 为什么需要泛型
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:没有泛型的问题
// Without generics - List``<dynamic>`` loses type safety
void main() {
List amounts = [1500, 3200, 890]; // List``<dynamic>``
amounts.add('not a number'); // No compile error!
for (final a in amounts) {
print((a as int) * 2); // Runtime crash on 'not a number'
}
}
// With generics - compile-time type safety
void main() {
List``<int>`` amounts = [1500, 3200, 890];
// amounts.add('not a number'); // Compile error!
for (final a in amounts) {
print(a * 2); // Safe - type is known
}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
4. 泛型类
(1) 泛型类定义
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:泛型容器类
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;
// Method that returns the generic type
T get dataOrThrow {
if (isSuccess && data != null) return data;
throw Exception(error ?? 'Unknown error');
}
// Transform the success value
Result``<R>`` map``<R>``(R Function(T) fn) {
if (isSuccess && data != null) {
return Result.success(fn(data));
}
return Result.failure(error);
}
}
void main() {
final success = Result.success(1500.0);
final failure = Result``<double>``.failure('Network timeout');
print(success.data); // 1500.0
print(failure.error); // Network timeout
// Map transforms the success value
final formatted = success.map((v) => '\$${v.toStringAsFixed(2)} USD');
print(formatted.data); // $1500.00 USD
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:泛型缓存类
class Cache``<T>`` {
final Map<String, T> _store = {};
final Duration _ttl;
Cache({Duration ttl = const Duration(minutes: 5)}) : _ttl = ttl;
void put(String key, T value) => _store[key] = value;
T? get(String key) => _store[key];
bool contains(String key) => _store.containsKey(key);
void remove(String key) => _store.remove(key);
void clear() => _store.clear();
}
void main() {
final orderCache = Cache<Map<String, dynamic>>();
orderCache.put('ORD-001', {'amount': 1500.0, 'status': 'completed'});
print(orderCache.get('ORD-001')); // {amount: 1500.0, status: completed}
print(orderCache.contains('ORD-002')); // false
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
5. 泛型函数
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:泛型函数定义
// Generic function with single type parameter
T firstOrNull``<T>``(List``<T>`` items) => items.isEmpty ? throw StateError('Empty list') : items.first;
// Generic function with two type parameters
R transform<T, R>(T input, R Function(T) converter) => converter(input);
// Generic function with constraint
double sumNumbers<T extends num>(List``<T>`` items) =>
items.fold(0.0, (sum, item) => sum + item.toDouble());
void main() {
print(firstOrNull(['ORD-001', 'ORD-002'])); // ORD-001
print(firstOrNull([1500, 3200])); // 1500
final formatted = transform(1500.0, (v) => '\$${v} USD');
print(formatted); // $1500.0 USD
print(sumNumbers([1, 2, 3, 4, 5])); // 15.0
print(sumNumbers([1.5, 2.5, 3.0])); // 7.0
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:泛型工具函数
// Safe cast utility
T? tryCast``<T>``(dynamic value) {
if (value is T) return value;
return null;
}
// Partition a list into two groups
(List``<T>``, List``<T>``) partition``<T>``(List``<T>`` items, bool Function(T) predicate) {
final matching = ``<T>``[];
final notMatching = ``<T>``[];
for (final item in items) {
(predicate(item) ? matching : notMatching).add(item);
}
return (matching, notMatching);
}
void main() {
// Safe cast
final intVal = tryCast``<int>``('hello'); // null
final strVal = tryCast``<String>``('hello'); // hello
// Partition
final (high, low) = partition([1500.0, 50.0, 3200.0, 890.0], (v) => v >= 1000);
print('High: $high'); // [1500.0, 3200.0]
print('Low: $low'); // [50.0, 890.0]
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
6. 泛型约束
(1) extends 单约束
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:extends 约束
// Only accepts types that extend num
double average<T extends num>(List``<T>`` values) {
if (values.isEmpty) return 0;
final sum = values.fold``<num>``(0, (a, b) => a + b);
return sum / values.length;
}
// Only accepts types that implement Comparable
T findMax<T extends Comparable>(List``<T>`` items) {
if (items.isEmpty) throw StateError('Empty list');
return items.reduce((a, b) => a.compareTo(b) >= 0 ? a : b);
}
void main() {
print(average([10, 20, 30])); // 20.0
print(average([1.5, 2.5, 3.0])); // 2.333...
print(findMax(['banana', 'apple', 'cherry'])); // cherry
print(findMax([10, 5, 8])); // 10
// average(['a', 'b']); // Compile error! String doesn't extend num
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
(2) 多约束
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:多约束
// Multiple constraints using intersection
class SortedCollection<T extends Comparable``<T>``> {
final List``<T>`` _items = [];
void add(T item) {
final index = _items.indexWhere((e) => item.compareTo(e) <= 0);
if (index == -1) {
_items.add(item);
} else {
_items.insert(index, item);
}
}
List``<T>`` get items => List.unmodifiable(_items);
}
void main() {
final sorted = SortedCollection``<String>``();
sorted.add('cherry');
sorted.add('apple');
sorted.add('banana');
print(sorted.items); // [apple, banana, cherry]
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
7. 泛型与集合协作
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:类型安全的集合操作
// Type-safe key-value store
class KeyValueStore<K, V> {
final Map<K, V> _data = {};
void put(K key, V value) => _data[key] = value;
V? get(K key) => _data[key];
// Convert all values
Map<K, R> mapValues``<R>``(R Function(V) converter) {
return _data.map((key, value) => MapEntry(key, converter(value)));
}
// Filter by key type
Map<K, V> whereKey(bool Function(K) predicate) {
return Map.fromEntries(
_data.entries.where((e) => predicate(e.key)),
);
}
}
void main() {
final store = KeyValueStore<String, double>();
store.put('Electronics', 3600.0);
store.put('Books', 170.0);
store.put('Clothing', 890.0);
// Map values to formatted strings
final formatted = store.mapValues((v) => '\$${v.toStringAsFixed(2)} USD');
print(formatted); // {Electronics: $3600.00 USD, Books: $170.00 USD, Clothing: $890.00 USD}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
8. Bob 场景:泛型数据转换器
classDiagram
class DataTransformer~T, R~ {
+transform(T input) R
+batch(List~T~ inputs) List~R~
+validate(T input) bool
}
class OrderParser {
+transform(String) Order
}
DataTransformer <|-- OrderParser
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:DataTransformer 泛型框架
// Base transformer interface
abstract class DataTransformer<T, R> {
R transform(T input);
String get name;
List``<R>`` batch(List``<T>`` inputs) {
final results = ``<R>``[];
final errors = <(int, String)>[];
for (var i = 0; i < inputs.length; i++) {
try {
if (validate(inputs[i])) {
results.add(transform(inputs[i]));
}
} catch (e) {
errors.add((i, e.toString()));
}
}
if (errors.isNotEmpty) {
print('$name: ${errors.length} errors during batch transform');
}
return results;
}
bool validate(T input) => true;
}
// Concrete transformer: String CSV line → Order
class Order {
final String id;
final double amount;
Order({required this.id, required this.amount});
@override
String toString() => 'Order($id, \$${amount.toStringAsFixed(2)})';
}
class CsvOrderTransformer extends DataTransformer<String, Order> {
@override
String get name => 'CsvOrderTransformer';
@override
bool validate(String input) {
final parts = input.split(',');
return parts.length >= 2;
}
@override
Order transform(String input) {
final parts = input.split(',');
return Order(
id: parts[0].trim(),
amount: double.parse(parts[1].trim()),
);
}
}
void main() {
final transformer = CsvOrderTransformer();
final csvLines = [
'ORD-001, 1500.0',
'ORD-002, 3200.0',
'ORD-003, 890.0',
];
final orders = transformer.batch(csvLines);
print('Transformed ${orders.length} orders:');
for (final order in orders) {
print(' $order');
}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
9. 完整示例:DataPipeline 泛型处理框架
// ============================================
// DataPipeline Generic Processing Framework
// Type-safe data transformation pipeline
// ============================================
// Result type for safe error handling
class Result``<T>`` {
final T? data;
final String? error;
Result.success(this.data) : error = null;
Result.failure(this.error) : data = null;
bool get isSuccess => data != null;
Result``<R>`` map``<R>``(R Function(T) fn) {
if (isSuccess && data != null) return Result.success(fn(data!));
return Result.failure(error);
}
}
// Generic transformer
abstract class DataTransformer<T, R> {
R transform(T input);
String get name;
bool validate(T input) => true;
Result``<R>`` safeTransform(T input) {
try {
if (!validate(input)) {
return Result.failure('$name: Validation failed');
}
return Result.success(transform(input));
} catch (e) {
return Result.failure('$name: $e');
}
}
List<Result``<R>``> batch(List``<T>`` inputs) =>
inputs.map(safeTransform).toList();
}
// Generic aggregator
abstract class DataAggregator<T, R> {
R aggregate(List``<T>`` items);
String get name;
}
// Sum aggregator for numeric types
class SumAggregator<T extends num> extends DataAggregator<T, double> {
@override
String get name => 'SumAggregator';
@override
double aggregate(List``<T>`` items) =>
items.fold(0.0, (sum, item) => sum + item.toDouble());
}
// Average aggregator
class AverageAggregator<T extends num> extends DataAggregator<T, double> {
@override
String get name => 'AverageAggregator';
@override
double aggregate(List``<T>`` items) =>
items.isEmpty ? 0 : items.fold(0.0, (s, i) => s + i.toDouble()) / items.length;
}
// Pipeline that chains transformers
class Pipeline<I, M, O> {
final DataTransformer<I, M> _first;
final DataTransformer<M, O> _second;
Pipeline(this._first, this._second);
List<Result``<O>``> process(List``<I>`` inputs) {
final midResults = _first.batch(inputs);
final midValues = midResults.where((r) => r.isSuccess).map((r) => r.data!).toList();
return _second.batch(midValues);
}
}
// Concrete types
class Order {
final String id;
final double amount;
Order({required this.id, required this.amount});
@override
String toString() => 'Order($id, \$${amount.toStringAsFixed(2)})';
}
class OrderSummary {
final String id;
final String tier;
OrderSummary({required this.id, required this.tier});
@override
String toString() => 'OrderSummary($id, $tier)';
}
class CsvToOrder extends DataTransformer<String, Order> {
@override
String get name => 'CsvToOrder';
@override
Order transform(String input) {
final parts = input.split(',');
return Order(id: parts[0].trim(), amount: double.parse(parts[1].trim()));
}
@override
bool validate(String input) => input.split(',').length >= 2;
}
class OrderToSummary extends DataTransformer<Order, OrderSummary> {
@override
String get name => 'OrderToSummary';
@override
OrderSummary transform(Order input) {
final tier = input.amount >= 1000 ? 'Premium' : 'Standard';
return OrderSummary(id: input.id, tier: tier);
}
}
void main() {
final csvLines = [
'ORD-001, 1500.0',
'ORD-002, 50.0',
'ORD-003, 3200.0',
];
// Simple transformer
final parser = CsvToOrder();
final orders = parser.batch(csvLines);
print('=== Parsed Orders ===');
for (final result in orders) {
if (result.isSuccess) {
print(' ${result.data}');
} else {
print(' Error: ${result.error}');
}
}
// Pipeline: CSV → Order → Summary
final pipeline = Pipeline<String, Order, OrderSummary>(
CsvToOrder(), OrderToSummary());
final summaries = pipeline.process(csvLines);
print('\n=== Order Summaries ===');
for (final result in summaries) {
if (result.isSuccess) print(' ${result.data}');
}
// Aggregation
final validAmounts = orders
.where((r) => r.isSuccess)
.map((r) => r.data!.amount)
.toList();
final total = SumAggregator().aggregate(validAmounts);
final avg = AverageAggregator().aggregate(validAmounts);
print('\nRevenue: \$${total.toStringAsFixed(2)} USD');
print('Average: \$${avg.toStringAsFixed(2)} USD');
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
输出:
=== Parsed Orders ===
Order(ORD-001, $1500.00)
Order(ORD-002, $50.00)
Order(ORD-003, $3200.00)
=== Order Summaries ===
OrderSummary(ORD-001, Premium)
OrderSummary(ORD-002, Standard)
OrderSummary(ORD-003, Premium)
Revenue: $4750.00 USD
Average: $1583.33 USD
❓ 常见问题
Q:泛型类型参数在运行时存在吗? A:Dart 的泛型是具化的(reified),运行时可以获取类型信息。`list is List``
``` 在运行时返回 true,这与 Java 的类型擦除不同。
Q:泛型约束可以用多个吗? A:可以。用
T extends A & B语法(Dart 暂不支持 & 多约束语法),但可以通过让约束类型同时实现多个接口来间接实现。或用 `T extends Comparable````` 这类方式。
Q:什么时候用泛型,什么时候用 dynamic? A:能用泛型就用泛型。泛型在编译时检查类型,dynamic 在运行时才发现错误。只有无法确定类型时(如 JSON 解析)才用 dynamic。
Q:泛型方法可以有不同的类型参数吗? A:可以。如
<T, R> R transform(T input, R Function(T) fn)有两个类型参数。方法的类型参数独立于类的类型参数。
Q:List
<dynamic>和 List<T>有什么区别? A:List``<dynamic>``` 接受任意类型元素,丢失类型安全;List<T>``` 只接受 T 类型元素,编译器保证类型安全。`List不是其他 `List``<T>的父类型。
Q:extends 和 implements 在泛型约束中有区别吗? A:约束中
extends既匹配类继承也匹配接口实现。T extends Comparable表示 T 可以是 Comparable 的子类或实现类。
Q:covariant 关键字有什么用? A:covariant 允许子类中参数类型使用更具体的类型(协变)。通常用于消费者模式的参数,但会减弱类型安全,谨慎使用。
📖 小节
- 泛型通过类型参数
<T>让代码复用于多种类型,编译器保证类型安全 - 泛型类(
class Box<T>)和泛型函数(T first<T>(List<T>))是最常用的形式 extends约束限制类型参数范围,如<T extends num>只接受数值类型- 泛型集合(
List<T>、Map<K, V>)比List<dynamic>更安全 - DataPipeline 的
DataTransformer<T, R>是泛型设计的典型应用
📝 作业
- 基础题(难度⭐):实现一个泛型
Pair<T, U>类,包含 first 和 second 两个字段,以及swap()方法返回交换后的 Pair。测试Pair<int, String>和Pair<double, bool>。 - 进阶题(难度⭐⭐):实现一个
Result<T>类型(类似 Rust 的 Result),包含isSuccess、data、error,以及map<R>()方法。用它重写一个可能失败的解析函数。 - 挑战题(难度⭐⭐⭐):实现泛型
Pipeline<T, R>类,支持链式添加多个DataTransformer,自动推断中间类型,最终调用process()执行整个管道。提示:可以用递归类型或 builder 模式。