Menu
CoddyTech

泛型约束

本课属于 Coddy Dart 学习旅程的“面向对象编程”单元。第 67 课,共 110 课。

有时,你需要让泛型具备某些能力。例如,如果你想比较项目或访问特定属性,不受限制的 T 并不能保证这些功能存在。泛型约束可以将类型参数限制为特定类型或其子类。

使用 extends 关键字来限制类型参数:

class NumberBox<T extends num> {
  T value;
  
  NumberBox(this.value);
  
  T doubled() => (value * 2) as T;
}

void main() {
  var intBox = NumberBox<int>(5);
  var doubleBox = NumberBox<double>(3.5);
  
  print(intBox.doubled());    // 10
  print(doubleBox.doubled()); // 7.0
  
  // var stringBox = NumberBox<String>('hi');  // 错误!
}

因为 T extends num,编译器知道 value 支持乘法等数值运算。没有这个约束,调用 value * 2 就会失败。

约束也适用于你自己的类层次结构:

abstract class Animal {
  String get name;
}

class Dog extends Animal {
  String get name => 'Dog';
}

class Shelter<T extends Animal> {
  List<T> animals = [];
  
  void add(T animal) => animals.add(animal);
  
  void printNames() {
    for (var animal in animals) {
      print(animal.name);  // 安全:T 保证 'name' 存在
    }
  }
}

泛型约束结合了泛型的灵活性,同时让你确定自己的类型支持哪些操作,从而保证安全性。

challenge icon

挑战

简单

我们来构建一个只能处理数值类型的统计计算器吧!你将创建一个带约束的泛型类,确保它只能用于数字,这样就能安全地对存储的值执行数学运算。

你将把代码分成两个文件:

  • stats.dart:创建一个泛型类 Stats<T extends num>,用于管理数值列表:
    • 一个名为 values 的 List<T>,用于存储数字
    • 一个使用给定值初始化列表的构造函数
    • 一个 sum() 方法,以 num 类型返回所有值的总和
    • 一个 min() 方法,以 T 类型返回最小值
    • 一个 max() 方法,以 T 类型返回最大值
    • 一个 printStats() 方法,以格式化的方式显示统计数据
  • main.dart:导入你的统计文件,并使用不同的数值类型演示这个带约束的泛型类:
    • 创建一个值为 [10, 25, 5, 30, 15] 的 Stats<int>,并调用 printStats()
    • 打印一个空行
    • 创建一个值为 [3.5, 1.2, 4.8, 2.1] 的 Stats<double>,并调用 printStats()

printStats() 方法应输出三行,分别显示总和、最小值和最大值。由于 T extends num,你可以安全地对这些值使用比较运算符和算术运算。如果泛型类型没有约束,就无法进行这些操作。

预期输出:

Sum: 85
Min: 5
Max: 30

Sum: 11.6
Min: 1.2
Max: 4.8

亲手试试

import 'stats.dart';

void main() {
  // TODO: 创建一个 Stats<int>,值为 [10, 25, 5, 30, 15]
  // 并调用 printStats()
  
  // TODO: 输出一个空行
  
  // TODO: 创建一个 Stats<double>,值为 [3.5, 1.2, 4.8, 2.1]
  // 并调用 printStats()
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Dart 在线编译器