擴充方法
擴充方法為現有程式庫新增功能。您可能在不知情的情況下使用擴充方法。例如,當您在 IDE 中使用程式碼完成時,它會建議擴充方法以及常規方法。
如果您喜歡觀看影片來學習,請查看此擴充方法概述。
概觀
#當您使用別人的 API 或實作廣泛使用的程式庫時,變更 API 通常不切實際或不可能。但您可能仍然想要新增一些功能。
例如,考慮以下將字串剖析為整數的程式碼
int.parse('42')
如果將該功能放在 String
上可能會更好 — 更短且更容易與工具一起使用
'42'.parseInt()
為了啟用該程式碼,您可以匯入一個包含 String
類別擴充的程式庫
import 'string_apis.dart';
// ···
print('42'.parseInt()); // Use an extension method.
擴充可以定義不只是方法,還有其他成員,例如 getter、setter 和運算子。此外,擴充可以有名稱,如果發生 API 衝突,這可能會很有用。以下是如何實作擴充方法 parseInt()
的範例,使用對字串進行操作的擴充 (名為 NumberParsing
)
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.
這通常是您使用擴充方法所需知道的全部內容。當您編寫程式碼時,您可能還需要知道擴充方法如何依賴靜態類型 (與 dynamic
相反),以及如何解決API 衝突。
靜態類型與動態
#您無法在類型為 dynamic
的變數上呼叫擴充方法。例如,以下程式碼會導致執行階段例外
dynamic d = '2';
print(d.parseInt()); // Runtime exception: NoSuchMethodError
擴充方法確實適用於 Dart 的類型推論。以下程式碼沒問題,因為變數 v
被推論為具有 String
類型
var v = '2';
print(v.parseInt()); // Output: 2
dynamic
無法運作的原因是擴充方法會根據接收者的靜態類型來解析。因為擴充方法是靜態解析的,所以它們與呼叫靜態函式一樣快。
如需有關靜態類型和 dynamic
的更多資訊,請參閱Dart 類型系統。
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.
print(NumberParsing('42').parseInt());
print(NumberParsing2('42').parseInt());
如果兩個擴充具有相同的名稱,則您可能需要使用前綴匯入
// 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.
// 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> { // <extension-name> is optional
(<member definition>)* // Can provide one or more <member definition>.
}
例如,以下說明如何在 String
類別上實作擴充
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
double parseDouble() {
return double.parse(this);
}
}
擴充的成員可以是方法、getter、setter 或運算子。擴充也可以具有靜態欄位和靜態輔助方法。若要存取擴充宣告外部的靜態成員,請透過宣告名稱呼叫它們,如類別變數和方法。
未命名擴充
#宣告擴充時,您可以省略名稱。未命名擴充僅在宣告它們的程式庫中可見。由於它們沒有名稱,因此無法顯式套用以解決API 衝突。
extension on String {
bool get isBlank => trim().isEmpty;
}
實作泛型擴充
#擴充可以具有泛型類型參數。例如,以下是一些使用 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 3.6.0。頁面上次更新於 2024-08-04。 檢視原始碼 或 回報問題。