Dart:拓展extension

  1. 基本语法
  2. 基本使用
    1. 如何理解extension StringExtension on String
  3. 给泛型类型添加扩展
  4. 扩展自己的类
  5. 命名扩展 & 冲突解决
  6. 扩展的限制

基本语法

扩展是 Dart 2.7 引入的特性,用来给现有的类添加新的方法、属性、操作符,而不用修改类本身,也不用继承它。

extension 扩展名 on 类型 {
  // 添加方法
  返回类型 方法名(参数) {
    ...
  }

  // 添加 getter
  返回类型 get 名称 => ...;

  // 添加 setter
  set 名称(类型 值) => ...;
}

基本使用

示例:给 String 添加方法


//StringExtension 现在不仅有自带的两个方法,还包含了String里面所有的方法
// 例如:contains、startsWith、endsWith 等等
extension StringExtension on String {
  /// 检查字符串是否是回文
  /// 回文是指正着读和反着读都一样的字符串
  /// 例如:'level'、'radar'、'madam' 等
  /// 注意:此方法忽略大小写和空格
  bool get isPalindrome {
    // 首先去除字符串中的空格,并转换为小写
    // \s+ 匹配一个或多个空格
    var cleaned = replaceAll(RegExp(r'\s+'), '').toLowerCase();
    // 然后检查清理后的字符串是否等于它的反转
    // split('') 将字符串拆分为字符列表,reversed 反转列表,join('') 将字符列表重新组合成字符串
    var reversed = cleaned.split('').reversed.join('');

    return cleaned == reversed;
  }

  /// 将字符串的首字母大写
  String capitalize() {
    // isEmpty 检查字符串是否为空
    // isEmpty 是一个 getter,返回 true 如果字符串长度为 0
    // 谁在调用 isEmpty?是 this,也就是当前字符串实例
    // 可以直接使用 this.isEmpty,或者使用 isEmpty
    // 省略 this 的规则是:如果在类内部访问属性或方法,可以省略 this
    // StringExtension 拓展方法中,this 指代当前字符串实例
    if (isEmpty) return this;
    return this[0].toUpperCase() + substring(1);
  }
}

void main() {
  print('level'.isPalindrome); // true
  print('dart'.capitalize()); // Dart
  // 还可以使用其他 String 方法
  // 下面这些方法都是 String 类自带的方法
  print('hello world'.contains('world')); // true
  print('hello world'.startsWith('hello')); // true
  print('hello world'.endsWith('world')); // true
}
  • on String 表示这是扩展 String 类型

  • 可以添加 getterisPalindrome)和普通方法(capitalize()

如何理解extension StringExtension on String

extension StringExtension on String {
  bool get isPalindrome => this == split('').reversed.join('');
}

上面的代码表面上就像是isPalindrome 这个新方法“加到了” String 类型上,这样在使用的时候就能写:

print('abcba'.isPalindrome); // true

但是本质上它并没有真的去修改 String(Dart 核心类是不可改的)。实际上是编译器看到'abcba'.isPalindrome 时,其实会悄悄替换成:

StringExtension('abcba').isPalindrome

也就是说,这只是编译期的语法糖,它让你看起来好像 String 自带了这个方法,但底层并不是改了 String 类本身。

给泛型类型添加扩展

extension ListSum<T extends num> on List<T> {
  T sum() {
    //reduce: 将列表中的所有元素通过指定的函数进行累加
    // (a + b) as T: 将 a 和 b 相加,并将结果转换为 T 类型
    // 不转换不行么? // 需要转换为 T 类型,因为 reduce 返回的是 dynamic 类型
    // 如果不转换为 T 类型,可能会导致类型不匹配的错误
    // The returned type 'num' isn't returnable from a 'T' function,
    // as required by the closure's context.
    return reduce((a, b) => (a + b) as T);
  }
}

void main() {
  // [1, 2, 3].sum() : 会调用 ListSum<int> 的 sum 方法
  // 会推断出 T 是 int 类型
  print([1, 2, 3].sum()); // 6
  print([1.5, 2.5].sum()); // 4.0
}

扩展自己的类

class Person {
  String name;
  Person(this.name);
}

// 给 Person 扩展新功能
extension PersonExtension on Person {
  void sayHello() {
    print('Hello, my name is $name');
  }

  String get upperName => name.toUpperCase();
}

void main() {
  var p = Person('Alice');
  p.sayHello();             // 调用扩展方法
  print(p.upperName);       // 调用扩展 getter
}

命名扩展 & 冲突解决

如果多个扩展有同名方法,Dart 会报方法冲突,此时:

  • 你可以显式调用扩展名来区分:
extension ExtA on String {
  String hello() => 'A: $this';
}

extension ExtB on String {
  String hello() => 'B: $this';
}

void main() {
  print(ExtA('dart').hello()); // A: dart
  print(ExtB('dart').hello()); // B: dart
}

扩展的限制

不能添加字段(实例变量),只能添加方法、getter、setter

不能重写现有方法(不会覆盖原方法)

不是真正修改类,只是语法糖,编译时会转化成静态方法调用


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

×

喜欢就点赞,疼爱就打赏