Flutter/Dart第19天:Dart高级特性之扩展方法(Extension methods)
Dart官方文档:https://dart.dev/language/extension-methods
重要说明:本博客基于Dart官网文档,但并不是简单的对官网进行翻译,在覆盖核心功能情况下,我会根据个人研发经验,加入自己的一些扩展问题和场景验证。
扩展方法概述
当我们使用了一些被广泛使用的其他库或者自己的库时,我们不太可能去修改这个库API,但是我们又想给库增加一些方法,该怎么办?如:我们想给String
类增加一些我自己常用的方法。
Dart作为一门集百家之长的编程语言,也考虑到了这个需求点,它提供了一个扩展方法(Extension methods)来解决问题问题。
如下代码样例,String
类型转换int
数字类型,常规的做法如下:
int.parse('123');
那如果String
类型提供一个转为int
数字类型的方法,是不是更好:
'123'.parseInt()
想要实现上诉目的,通过扩展String
类型,提供对应方法即可:
import './19-ntopic-string-apis.dart'; void main() { print('123'.parseInt()); // 结果:123 } // // 19-ntopic-string-apis.dart 内容 // extension NumberParsing on String { int parseInt() { return int.parse(this); } }
使用扩展方法
上一章节的最后,其实我们已经展示了如何定义和使用扩展方法。使用扩展方法,和使用类型的其他方法没有任何差异。
接下来我们来看看,扩展方法在静态类型和动态类型的使用,和如何解决同名扩展方法冲突。
静态类型和dynamic动态类型
特别注意:dynamic
动态类型禁止使用扩展方法!如下代码样例,会抛出NoSuchMethodError
运行时异常。
import './19-ntopic-string-apis.dart'; void main() { print('123'.parseInt()); // 结果:123 dynamic d = '2'; // NoSuchMethodError: Class 'String' has no instance method 'parseInt'. print(d.parseInt()); }
但是扩展方法可用于类型推导上,如下代码无任何问题,因为变量v
的类型推导成String
类型:
import './19-ntopic-string-apis.dart'; void main() { print('123'.parseInt()); // 结果:123 var v = '2'; print(v.parseInt()); // 结果:2 dynamic d = '2'; // NoSuchMethodError: Class 'String' has no instance method 'parseInt'. print(d.parseInt()); }
dynamic
动态类型不可用户扩展方法的原因是,扩展方法只能接收静态类型,因此调用扩展方法和调用静态方法一样高效。
扩展方法冲突
我们在应用中,会引入多个库,如果有多个库都对同类型增加了同名的拓展方法,那么就导出扩展方法冲突了。如:对String
类型,库A和库B都有pareInt()
扩展方法,那么这个扩展方法就存在冲突。
一般情况下,有3种方法来解决扩展方法的冲突:
第一种方法,在引入库时,通过show
或者hide
关键字限制扩展方法:
// String扩展方法:parseInt() import 'string_apis.dart'; // String扩展方法:parseInt(), `hide`隐藏扩展类型 import 'string_apis_2.dart' hide NumberParsing2; // ··· // 使用了 'string_apis.dart' 中定义的扩展方法:parseInt() print('42'.parseInt());
第二种方法,显示指定扩展类型的扩展方法:
// 扩展类型:NumberParsing,扩展方法:parseInt() import 'string_apis.dart'; // 扩展类型:NumberParsing2,扩展方法:parseInt() import 'string_apis_2.dart'; // 显示使用扩展类型 print(NumberParsing('42').parseInt()); print(NumberParsing2('42').parseInt());
第三种方法,假设第二种方法的扩展类型也一样,那么可在引入库增加前缀解决:
// 扩展类型:NumberParsing,扩展方法:parseInt() import 'string_apis.dart'; // 扩展类型:NumberParsing,扩展方法:parseInt(),parseNum() import 'string_apis_3.dart' as rad; // 'string_apis.dart' 扩展方法:parseInt() print(NumberParsing('42').parseInt()); // 'string_apis_3.dart' 扩展方法:parseInt() print(rad.NumberParsing('42').parseInt()); // 'string_apis_3.dart' 扩展方法:parseNum() print('42'.parseNum());
因为parseNum()
扩展方法不存在冲突,因此可直接使用。仅当存在扩展类型冲突时,才需要增加前缀。
实现扩展方法
在前面2个章节,其实已经提到了部分扩展方法的实现方法。扩展方法实现语法如下(扩展类型名是可选的):
extension <extension name>? on <type> { (<member definition>)* }
如下代码样例,对String
类型,增加了2个扩展方法(扩展类型名:NumberParsing
):
extension NumberParsing on String { int parseInt() { return int.parse(this); } double parseDouble() { return double.parse(this); } }
扩展类型中的成员,可以是方法、Getters、Setters和操作符,同时也可以是静态属性和静态方法,外围可通用普通类型静态属性和静态方法一样使用。
未命名的扩展类型
我们定义未命名的扩展,它们的可见范围仅在库内容(类似于私有属性和方法);由于扩展类型未命名,因此无法明确的用于冲突解决,它们的静态属性和静态方法,也只能在扩展内部使用:
extension on String { bool get isBlank => trim().isEmpty; }
实现泛型扩展
扩展也运用在泛型参数,如下代码样例,对List<T>
增加扩展方法和操作符,类型T在调用时才绑定静态类型:
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)]; }
我的本博客原地址:https://ntopic.cn/p/2023110401
本文作者:奔跑的蜗牛,转载请注明原文链接:https://ntopic.cn
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 普通人也能轻松掌握的20个DeepSeek高频提示词(2025版)
· DeepSeek+Zotero