泛型

泛型即参数化类型,它是一个既常见又重要的编程语言特性。本文将举例说明泛型的优势和具体使用方法。

类型安全

泛型的优势之一是类型安全

// ex611.dart
void main() {
  var list = [];
  list.add(1);
  list.add('A');
  print('$list ${list.runtimeType}'); // Output: [1, A] List<dynamic>
}

此例中list的类型为List<dynamic>,表示list可以包含任何类型的数据。如果将list声明为 List<int>将引起编译错误。

// ex611a.dart
void main() {
  var list = <int>[];
  list.add(1);
  list.add('A');
  // Error: The argument type 'String' can't be assigned to the parameter type 'int'.
}

减少代码重复

泛型的另一个优势是减少代码重复。实战中经常要将来自设备或网络的数据缓存起来。下面的代码创建一个用于缓存String数据的类。

// ex612.dart
class StringCache {
  final String data;

  StringCache(this.data);
}

使用相同的代码结构可为doubleint及其它数据类型的数据创建对应的缓存类。例如:

// ex612a.dart
class DoubleCache {
  final double data;

  DoubleCache(this.data);
}

通过使用泛型技术我们只需要创建一个类,就可以 cover 此类应用场景。

// ex613.dart
class Cache<T> { // 1
  final T data; // 2

  Cache(this.data);
}

void main() {
  var str = Cache('Dart'); // 3
  var num = Cache(12); // 4
  assert(str.data == 'Dart');
  assert(num.data == 12);
}
  1. 泛型的类型参数T写在一对尖括号(<>)中;
  2. 字段data的数据类型被声明为T
  3. 得益于 Dart 的自动类型推断,Cache('Dart')Cache<String>('Dart'),类型参数T将在编译时被替换为String
  4. 同上,Cache(12)Cache<int>(12)

限制参数化类型

使用extends关键字让参数化类型为指定类型的子类型。

// ex614.dart
class Cache<T extends Object> {
  final T data;

  Cache(this.data);
}

void main() {
  Cache(null);
  // Error: The argument type 'Null' can't be assigned to the parameter type 'Object'.
}

此例中的TObject 的子类,它不能是 Null 类型。

泛型方法

参数化类型也可用在方法或函数里,语法是将类型参数连通一对尖括号,写在方法或函数名后面。

// ex615.dart
class Example {
  static T max<T extends Comparable<T>>(T a, T b) => a.compareTo(b) > 0 ? a : b;
}

T min<T extends Comparable<T>>(T a, T b) => a.compareTo(b) < 0 ? a : b;

void main() {
  print(Example.max(12, 34)); // Output: 34
  print(min('apple', 'orange')); // Output: apple
}

参考资料