▩Dart-Extension方法

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了▩Dart-Extension方法相关的知识,希望对你有一定的参考价值。

目录


Dart 2.7中引入的扩展方法是向现有库添加功能的一种方法。你可能会在不知情的情况下使用扩展方法。例如,当你在IDE中使用代码完成时,它建议使用扩展方法和常规方法。

一、概述

当你使用其他人的API或实现广泛使用的库时,更改API通常是不切实际或不可能的。但你可能仍然希望添加一些功能。

例如,考虑将字符串解析成整数的以下代码:

int.parse('42')

将该功能放在字符串上可能会更好—更短,更易于与工具一起使用:

'42'.parseInt()

要启用该代码,可以导入包含String类扩展的库:

import 'string_apis.dart';
// ···
print('42'.parseInt()); // Use an extension method.

扩展不仅可以定义方法,还可以定义其他成员,如getter、setter和操作符。此外,扩展名也有名称,如果出现API冲突,这会很有帮助。下面是使用对字符串进行操作的扩展(名为NumberParsing)实现扩展方法parseInt()的方法:

extension NumberParsing on String 
	int parseInt() 
		return int.parse(this);
	
	// ...

下一节介绍如何使用扩展方法。之后是关于实现扩展方法的部分。

二、使用扩展方法

与所有Dart代码一样,扩展方法也在库中。你已经了解了如何使用扩展方法——只需导入它所在的库,并像普通方法一样使用它:

// Import a library that contains an extension on String.
import 'string_apis.dart';
// ···
print('42'.padLeft(5)); // Use a String method.
print('42'.parseInt()); // Use an extension method.

这就是使用扩展方法通常需要知道的全部内容。在编写代码时,你可能还需要知道扩展方法如何依赖于静态类型(而不是动态类型),以及如何解决API冲突。

1. 静态类型和dynamic

不能对dynamic类型的变量调用扩展方法。例如,以下代码导致运行时异常:

dynamic d = '2';
// parseInt()是个[扩展方法]
print(d.parseInt()); // Runtime exception: NoSuchMethodError

dynamic不起作用的原因是**[扩展方法]是针对接收器的[静态类型]来解决的**。因为扩展方法是静态解析的,所以它们与调用静态函数一样快。

扩展方法与Dart的类型推断一起工作。以下代码很好,因为变量v被推断为具有类型String

var v = '2';
print(v.parseInt());// 2
2. API冲突

如果扩展成员与接口或其他扩展成员冲突,那么你有几个选项。

一个选项是更改导入冲突扩展的方式,使用“显示(show)”或“隐藏(hide)”来限制公开的API:

// 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;
// ···
// Uses the parseInt() defined in 'string_apis.dart'.
print('42'.parseInt());

另一个选项是显式应用扩展,这会导致代码看起来像是一个包装类:

// 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.
// ···
<!--print('42'.parseInt()); ——>Doesn't work.Because confict.-->
print(NumberParsing('42').parseInt());
print(NumberParsing2('42').parseInt());

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

// 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;// 要调用的方法名称相同,用别名
// ···
<!--print('42'.parseInt()); ——>Doesn't work.Because confict.-->
// 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());

如示例所示,即使使用前缀导入,也可以隐式调用扩展方法。唯一需要使用前缀的时候是避免显式调用扩展时出现名称冲突。

三、实现扩展方法

使用以下语法创建扩展:

extension <extension name> on <type> 
	(<member definition>)*

例如,以下是如何在String类上实现扩展:

extension NumberParsing on String 
  int parseInt() 
    return int.parse(this);
  
  double parseDouble() 
    return double.parse(this);
  

要创建仅在声明扩展的库中可见的本地扩展,请省略扩展名或为其指定一个以下划线(_)开头的名称。

扩展的成员可以是方法、getter、setter和操作符。扩展还可以有静态字段和静态辅助方法。

四、实现泛型扩展

扩展可以具有泛型类型参数。例如,下面的一些代码使用getter、运算符和方法扩展内置List<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)];

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

五、更多

有关扩展方法的详细信息,请参见以下内容:

以上是关于▩Dart-Extension方法的主要内容,如果未能解决你的问题,请参考以下文章

类型方法和类型实例方法等之间的区别?

类型方法和类型实例方法等之间的区别?

js实例Array类型的9个数组方法,Date类型的41个日期方法,Function类型

数据类型的内置方法 可变类型与不可变类型

方法入参和返回值介绍

JS高程5.引用类型Array类型的位置方法,迭代方法,归并方法