Android学Dart学习笔记第二十二节 类-扩展方法

概览

当您使用他人的 API 或实现一个被广泛使用的库时,修改 API 常常不切实际甚至根本不可能。但您可能仍然希望为其添加某些功能。

例如,考虑下面这段将字符串解析为整数的代码:

dart 复制代码
int.parse('42')

如果这个功能能直接放在 String 类上,可能会更好------更简短且便于使用工具处理

dart 复制代码
'42'.parseInt()

为了使上述代码生效,您可以导入一个包含 String 类扩展的库:

dart 复制代码
import 'string_apis.dart';

void main() {
  print('42'.parseInt()); // Use an extension method.
}

扩展不仅能定义方法,还可以定义其他成员,例如 getter、setter 和运算符。此外,扩展可以拥有名称,这在出现 API 冲突时非常有用。以下是通过一个作用于字符串的扩展(名为 NumberParsing)来实现扩展方法 parseInt() 的方式:

dart 复制代码
extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }
}

如何使用扩展方法

与所有 Dart 代码一样,扩展方法也存在于库中。您已经了解了如何使用扩展方法------只需导入它所在的库,然后像使用普通方法一样调用它即可:

dart 复制代码
// Import a library that contains an extension on String.
import 'string_apis.dart';

void main() {
  print('42'.padLeft(5)); // Use a String method.
  print('42'.parseInt()); // Use an extension method.
}

通常,了解这些就足以使用扩展方法了。在编写代码时,您可能还需要了解扩展方法如何依赖于静态类型(与动态类型相对),以及如何解决 API 冲突。

Static types and dynamic

您不能在类型为 dynamic 的变量上调用扩展方法。例如,下面的代码会导致运行时异常:

dart 复制代码
dynamic d = '2';
print(d.parseInt()); // Runtime exception: NoSuchMethodError

扩展方法可以与 Dart 的类型推断协同工作。以下代码是有效的,因为变量 v 被推断为 String 类型:

dart 复制代码
var v = '2';
print(v.parseInt()); // Output: 2

dynamic 类型无法使用扩展方法的原因是:扩展方法是根据接收者的静态类型来解析的。由于扩展方法是静态解析的,因此其调用速度与调用静态函数一样快。

如何解决API冲突

如果一个扩展成员与接口或其他扩展成员发生冲突,您有几种处理方式。

一种选择是改变导入冲突扩展的方式,使用 show 或 hide 来限制暴露的 API:

dart 复制代码
// Defines the String extension method parseInt().
import 'string_apis.dart';

// Also defines parseInt(), but hiding NumberParsing2
// hides that extension method.
import 'string_apis_2.dart' hide NumberParsing2;

void main() {
  // Uses the parseInt() defined in 'string_apis.dart'.
  print('42'.parseInt());
}

另一种选择是显式地应用扩展,这样会使代码看起来像是扩展充当了一个包装类:

dart 复制代码
// Both libraries define extensions on String that contain parseInt(),
// and the extensions have different names.
import 'string_apis.dart'; // Contains NumberParsing extension.
import 'string_apis_2.dart'; // Contains NumberParsing2 extension.

void main() {
  // print('42'.parseInt()); // Doesn't work.
  print(NumberParsing('42').parseInt());
  print(NumberParsing2('42').parseInt());
}

如果两个扩展具有相同的名称,您可能需要使用前缀来导入:

dart 复制代码
// Both libraries define extensions named NumberParsing
// that contain the extension method parseInt(). One NumberParsing
// extension (in 'string_apis_3.dart') also defines parseNum().
import 'string_apis.dart';
import 'string_apis_3.dart' as rad;

void main() {
  // print('42'.parseInt()); // Doesn't work.

  // Use the ParseNumbers extension from string_apis.dart.
  print(NumberParsing('42').parseInt());

  // Use the ParseNumbers extension from string_apis_3.dart.
  print(rad.NumberParsing('42').parseInt());

  // Only string_apis_3.dart has parseNum().
  print('42'.parseNum());
}

如示例所示,即使使用前缀导入,您仍可以隐式调用扩展方法。唯一需要显式使用前缀的情况,是在显式调用扩展方法时为了避免名称冲突。

如何定义扩展方法

使用以下语法来创建扩展:

dart 复制代码
extension <extension name>? on <type> { // <extension-name> is 可选的
  (<member definition>)* // 可以提供一个或多个 <成员定义>。
}

以下是在 String 类上实现扩展的方式:

dart 复制代码
extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }

  double parseDouble() {
    return double.parse(this);
  }

}

扩展的成员可以是方法、getter、setter 或运算符。扩展也可以拥有静态字段和静态辅助方法。要在扩展声明之外访问静态成员,需像调用类变量和类方法一样,通过声明名称来调用它们。

未命名扩展

在声明扩展时,可以省略名称。无名称扩展仅在其声明的库中可见。由于它们没有名称,因此无法通过显式应用来解决 API 冲突。

dart 复制代码
extension on String {
  bool get isBlank => trim().isEmpty;
}

下划线不算未命名

且 未命名扩展内部的静态成员无法被其他地方调用。

泛型扩展

扩展可以拥有泛型类型参数。例如,以下代码使用一个 getter、一个运算符和一个方法来扩展内置的 List 类型:

dart 复制代码
extension MyFancyList<T> on List<T> {
  int get doubleLength => length * 2;
  List<T> operator -() => reversed.toList();
  List<List<T>> split(int at) => [sublist(0, at), sublist(at)];
}

类型 T 是基于调用这些方法的列表的静态类型来绑定的。

相关推荐
csj502 小时前
安卓基础之《(7)—中级控件(1)图形定制》
android
TL滕2 小时前
从0开始学算法——第二十天(简易搜索引擎)
笔记·学习·算法
子榆.2 小时前
【2025 最新实践】Flutter 与 OpenHarmony 的“共生模式”:如何构建跨生态应用?(含完整项目架构图 + 源码)
flutter·华为·智能手机·electron
Android系统攻城狮2 小时前
Android ALSA驱动进阶之设置访问掩码snd_pcm_access_mask_set:用法实例(九十九)
android·pcm·音频进阶·alsa驱动·android驱动
kirk_wang2 小时前
Flutter 三方库在 OHOS 平台的适配实践:以 flutter_test_lib 为例
flutter·移动开发·跨平台·arkts·鸿蒙
你好~每一天2 小时前
数据分析专员:当传统汽车销售融入AI智能,如何驱动业绩新增长
大数据·数据结构·人工智能·学习·数据分析·汽车·高性价比
✎ ﹏梦醒͜ღ҉繁华落℘2 小时前
计算机网络学习(三)-- IP地址 和 MAC 地址如何转换,以太网
学习·tcp/ip·计算机网络
hudawei9962 小时前
kotlin冷流热流的区别
android·开发语言·kotlin·flow··冷流·热流
巴拉巴拉~~2 小时前
KMP 算法通用折叠面板组件:KmpExpandablePanelWidget 平滑动画 + 单 / 多面板 + 全样式自定义
flutter