擴充方法
擴充方法為現有的函式庫新增功能。您可能在不知情的情況下使用擴充方法。例如,當您在 IDE 中使用程式碼完成功能時,它會建議擴充方法以及常規方法。
如果觀看影片有助於您學習,請查看此擴充方法概觀。
總覽
#當您使用別人的 API 或實作廣泛使用的函式庫時,通常不切實際或不可能變更 API。但您可能仍然想要新增一些功能。
例如,考慮以下將字串剖析為整數的程式碼
int.parse('42')
如果讓該功能在 String
上會更好—更簡短且更易於工具使用
'42'.parseInt()
若要啟用該程式碼,您可以匯入包含 String
類別擴充的函式庫
import 'string_apis.dart';
void main() {
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';
void main() {
print('42'.padLeft(5)); // Use a String method.
print('42'.parseInt()); // Use an extension method.
}
這通常是您需要了解的所有使用擴充方法的方式。當您編寫程式碼時,您可能還需要了解擴充方法如何依賴靜態類型(相對於 dynamic
)以及如何解決 API 衝突。
靜態類型和 dynamic
#您無法在 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;
void main() {
// 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.
void main() {
// 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;
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());
}
如範例所示,即使您使用前綴匯入,也可以隱含地調用擴充方法。您需要使用前綴的唯一時機是在明確調用擴充時避免名稱衝突。
實作擴充方法
#使用以下語法建立擴充
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.7.1 版本。頁面上次更新於 2025-02-12。 檢視原始碼 或回報此頁面的問題。