Dart:泛型

  1. 为什么要有泛型
    1. 假如没有泛型
    2. 泛型的引入
    3. 泛型的优势
  2. 常见泛型用法
    1. 泛型与集合
    2. 类的泛型
    3. 方法的泛型
    4. 限定泛型(泛型约束)
    5. 泛型的类型推断
  3. 小心的坑

为什么要有泛型

假如没有泛型

假设你写一个保存对象的容器类,如果不用泛型,你可能这样写:

class Box {
  Object? content;
}

void main() {
  var box = Box();
  box.content = 123; // int
  box.content = "Hello"; // String

  // 读取时必须手动转换
  String text = box.content as String; 
}

问题:

  • 不安全:任何类型都能存进去,可能取出来出错(as 转型失败)。
  • 代码冗余:不同类型需要反复写强制类型转换。

泛型的引入

有了泛型,我们可以这样写:

class Box<T> {
  T? content;
}

void main() {
  var intBox = Box<int>();
  intBox.content = 123;
  // intBox.content = "Hello"; // ❌ 编译期报错

  var strBox = Box<String>();
  strBox.content = "Hello";
}
  • <T> 是类型参数,T 在使用时被具体类型替换。
  • 编译器会在编译期检查类型,避免运行时错误。

泛型的优势

  • 类型安全:编译期检查,避免运行时错误。
  • 代码复用:不用为不同类型重复写逻辑。
  • 性能优化:避免 Object + 强制类型转换的开销。

常见泛型用法

泛型与集合

Dart 的集合类本身就是泛型:

List<int> numbers = [1, 2, 3];
Map<String, double> prices = {"apple": 3.5, "banana": 2.0};

如果不用泛型:

List items = [1, "two", 3.0]; // 动态类型,取出来要强转

类的泛型

class Pair<K, V> {
  K key;
  V value;
  Pair(this.key, this.value);
}

void main() {
  var pair = Pair<String, int>("age", 25);
  print("${pair.key}: ${pair.value}");
}
  • 可以有多个泛型参数(KV)。

方法的泛型

泛型不仅能写在类上,也可以写在方法上:

T firstElement<T>(List<T> list) {
  return list[0];
}

void main() {
  print(firstElement<int>([1, 2, 3])); // 1
  print(firstElement(["a", "b", "c"])); // 自动推断 T = String
}

限定泛型(泛型约束)

有时候你希望泛型必须是某个类的子类,这就需要 extends

class Animal {
  void speak() => print("Animal sound");
}

class Dog extends Animal {
  void speak() => print("Woof!");
}

void makeSpeak<T extends Animal>(T animal) {
  animal.speak();
}

void main() {
  makeSpeak(Dog()); // Woof!
  // makeSpeak("Hello"); // ❌ 编译期错误
}

泛型的类型推断

在绝大多数情况下 Dart 能自动推断泛型类型:

class Box<T> {
  T? content;
}
var box = Box("Hello"); // 推断为 Box<String>

但也可以显式指定:

class Box<T> {
  T? content;
}
var box = Box<int>(123);

小心的坑

  1. 泛型在运行时会被类型擦除

    • Dart 运行时不保留泛型参数类型,类型检查发生在编译期。

    • 例如:

      print([1, 2, 3] is List<int>); // true
      print([1, 2, 3] is List<String>); // false
      

      这里 Dart 做了特殊处理来保留类型信息,但底层并不是完整保留。

  2. 不能直接创建泛型类型的实例

    class Box<T> {
      // var content = T(); // ❌ 错误,不能直接 new T
    }
    

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com

×

喜欢就点赞,疼爱就打赏