Flutter/Dart第19天:Dart高级特性之扩展方法(Extension methods) ...

小秦哥  金牌会员 | 2023-12-4 12:53:31 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 880|帖子 880|积分 2640

Dart官方文档:https://dart.dev/language/extension-methods
重要说明:本博客基于Dart官网文档,但并不是简单的对官网进行翻译,在覆盖核心功能情况下,我会根据个人研发经验,加入自己的一些扩展问题和场景验证。
扩展方法概述

当我们使用了一些被广泛使用的其他库或者自己的库时,我们不太可能去修改这个库API,但是我们又想给库增加一些方法,该怎么办?如:我们想给String类增加一些我自己常用的方法。
Dart作为一门集百家之长的编程语言,也考虑到了这个需求点,它提供了一个扩展方法(Extension methods)来解决问题问题。
如下代码样例,String类型转换int数字类型,常规的做法如下:
  1. int.parse('123');
复制代码
那如果String类型提供一个转为int数字类型的方法,是不是更好:
  1. '123'.parseInt()
复制代码
想要实现上诉目的,通过扩展String类型,提供对应方法即可:
  1. import './19-ntopic-string-apis.dart';
  2. void main() {
  3.   print('123'.parseInt());
  4.   // 结果:123
  5. }
  6. //
  7. // 19-ntopic-string-apis.dart 内容
  8. //
  9. extension NumberParsing on String {
  10.   int parseInt() {
  11.     return int.parse(this);
  12.   }
  13. }
复制代码
使用扩展方法

上一章节的最后,其实我们已经展示了如何定义和使用扩展方法。使用扩展方法,和使用类型的其他方法没有任何差异。
接下来我们来看看,扩展方法在静态类型和动态类型的使用,和如何解决同名扩展方法冲突。
静态类型和dynamic动态类型

特别注意:dynamic动态类型禁止使用扩展方法!如下代码样例,会抛出NoSuchMethodError运行时异常。
  1. import './19-ntopic-string-apis.dart';
  2. void main() {
  3.   print('123'.parseInt());
  4.   // 结果:123
  5.   dynamic d = '2';
  6.   // NoSuchMethodError: Class 'String' has no instance method 'parseInt'.
  7.   print(d.parseInt());
  8. }
复制代码
但是扩展方法可用于类型推导上,如下代码无任何问题,因为变量v的类型推导成String类型:
  1. import './19-ntopic-string-apis.dart';
  2. void main() {
  3.   print('123'.parseInt());
  4.   // 结果:123
  5.   var v = '2';
  6.   print(v.parseInt());
  7.   // 结果:2
  8.   dynamic d = '2';
  9.   // NoSuchMethodError: Class 'String' has no instance method 'parseInt'.
  10.   print(d.parseInt());
  11. }
复制代码
dynamic动态类型不可用户扩展方法的原因是,扩展方法只能接收静态类型,因此调用扩展方法和调用静态方法一样高效。
扩展方法冲突

我们在应用中,会引入多个库,如果有多个库都对同类型增加了同名的拓展方法,那么就导出扩展方法冲突了。如:对String类型,库A和库B都有pareInt()扩展方法,那么这个扩展方法就存在冲突。
一般情况下,有3种方法来解决扩展方法的冲突:
第一种方法,在引入库时,通过show或者hide关键字限制扩展方法:
  1. // String扩展方法:parseInt()
  2. import 'string_apis.dart';
  3. // String扩展方法:parseInt(), `hide`隐藏扩展类型
  4. import 'string_apis_2.dart' hide NumberParsing2;
  5. // ···
  6. // 使用了 'string_apis.dart' 中定义的扩展方法:parseInt()
  7. print('42'.parseInt());
复制代码
第二种方法,显示指定扩展类型的扩展方法:
  1. // 扩展类型:NumberParsing,扩展方法:parseInt()
  2. import 'string_apis.dart';
  3. // 扩展类型:NumberParsing2,扩展方法:parseInt()
  4. import 'string_apis_2.dart';
  5. // 显示使用扩展类型
  6. print(NumberParsing('42').parseInt());
  7. print(NumberParsing2('42').parseInt());
复制代码
第三种方法,假设第二种方法的扩展类型也一样,那么可在引入库增加前缀解决:
  1. // 扩展类型:NumberParsing,扩展方法:parseInt()
  2. import 'string_apis.dart';
  3. // 扩展类型:NumberParsing,扩展方法:parseInt(),parseNum()
  4. import 'string_apis_3.dart' as rad;
  5. // 'string_apis.dart' 扩展方法:parseInt()
  6. print(NumberParsing('42').parseInt());
  7. // 'string_apis_3.dart' 扩展方法:parseInt()
  8. print(rad.NumberParsing('42').parseInt());
  9. // 'string_apis_3.dart' 扩展方法:parseNum()
  10. print('42'.parseNum());
复制代码
因为parseNum()扩展方法不存在冲突,因此可直接使用。仅当存在扩展类型冲突时,才需要增加前缀。
实现扩展方法

在前面2个章节,其实已经提到了部分扩展方法的实现方法。扩展方法实现语法如下(扩展类型名是可选的):
  1. extension <extension name>? on <type> {
  2.   (<member definition>)*
  3. }
复制代码
如下代码样例,对String类型,增加了2个扩展方法(扩展类型名:NumberParsing):
  1. extension NumberParsing on String {
  2.   int parseInt() {
  3.     return int.parse(this);
  4.   }
  5.   double parseDouble() {
  6.     return double.parse(this);
  7.   }
  8. }
复制代码
扩展类型中的成员,可以是方法、Getters、Setters和操作符,同时也可以是静态属性和静态方法,外围可通用普通类型静态属性和静态方法一样使用。
未命名的扩展类型

我们定义未命名的扩展,它们的可见范围仅在库内容(类似于私有属性和方法);由于扩展类型未命名,因此无法明确的用于冲突解决,它们的静态属性和静态方法,也只能在扩展内部使用:
  1. extension on String {
  2.   bool get isBlank => trim().isEmpty;
  3. }
复制代码
实现泛型扩展

扩展也运用在泛型参数,如下代码样例,对List增加扩展方法和操作符,类型T在调用时才绑定静态类型:
  1. extension MyFancyList<T> on List<T> {
  2.   int get doubleLength => length * 2;
  3.   List<T> operator -() => reversed.toList();
  4.   List<List<T>> split(int at) => [sublist(0, at), sublist(at)];
  5. }
复制代码
我的本博客原地址:https://ntopic.cn/p/2023110401

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

小秦哥

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表