Erweiterungsmethoden ermöglichen es, bestehenden Typen und Typen aus Drittbibliotheken neue Funktionen hinzuzufügen, ohne den Quellcode der ursprünglichen Klasse zu ändern. Die Codevervollständigung der IDE zeigt Erweiterungsmethoden gemeinsam mit nativen Methoden an.
Sie können von Dritten bereitgestellte APIs nicht ändern, möchten aber praktische Hilfsmethoden ergänzen.
Beispielanforderung: '42'.parseInt() statt int.parse('42') verwenden.
Syntaxaufbau
extension Erweiterungsname? on Zieltyp {
// Methoden, Getter, Setter, Operatoren, statische Member
}Code-Sprache: JavaScript (javascript)
Beispiel
// Alles in einer Datei schreiben, keine Aufteilung nötig, main wird direkt ausgeführt
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
double parseDouble() {
return double.parse(this);
}
}
void main() {
print('42'.parseInt()); // 42
print('3.14'.parseDouble()); // 3.14
print('100'.padLeft(6)); // Vergleich mit nativer String-Methode
}Code-Sprache: Dart (dart)
Ausgabeergebnis
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
42
3.14
100Code-Sprache: CSS (css)
Der Erweiterungsname ist optional; this steht für die Instanz, die die Erweiterung aufruft.
Unterstützte Member-Arten in Erweiterungen
Innerhalb einer Erweiterung lassen sich definieren: Instanzmethoden, Getter, Setter, Operatoren und statische Member.
extension StringExt on String {
// Getter
bool get isEmptyOrBlank => trim().isEmpty;
// Instanzmethode
String repeat(int count) => List.filled(count, this).join();
// Operatorüberladung
String operator *(int count) => repeat(count);
// Statisches Member (Zugriff über Erweiterungsnamen)
static String emptyStr = '';
static void printTip() => print("Statische Methode der String-Erweiterung");
}
void main() {
var str = " dart ";
print(str.isEmptyOrBlank);
print("hi" * 3);
print("hello".repeat(2));
// Zugriff auf statische Member
print(StringExt.emptyStr);
StringExt.printTip();
}Code-Sprache: PHP (php)
Ausgabeergebnis
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
false
hihihi
hellohello
Statische Methode der String-ErweiterungCode-Sprache: JavaScript (javascript)
Unbenannte Erweiterungen (Anonyme Erweiterungen)
Wenn Sie keinen Erweiterungsnamen angeben, ist diese nur innerhalb der aktuellen Bibliothek sichtbar. Namenskonflikte können nicht durch expliziten Aufruf aufgelöst werden.
Sie können sie von außen nicht mit der Syntax Erweiterungsname(Instanz) aufrufen.
extension on String {
bool get isBlank => trim().isEmpty;
}
void main() {
print(" ".isBlank); // true
}Code-Sprache: Dart (dart)
Statische Member einer anonymen Erweiterung können nur innerhalb der Erweiterung verwendet werden.
Generische Erweiterungen (Generic extensions)
Sie können Erweiterungen für generische Typen erstellen, zum Beispiel 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)];
}
void main() {
List<int> list = [1, 2, 3, 4];
print(list.doubleLength); // 8
print(-list); // [4,3,2,1]
print(list.split(2)); // [[1,2],[3,4]]
}Code-Sprache: Dart (dart)
Ausgabeergebnis
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
8
[4, 3, 2, 1]
[[1, 2], [3, 4]]Code-Sprache: CSS (css)
Statischer Typ static vs dynamic
Variablen vom Typ dynamic können keine Erweiterungsmethoden aufrufen
Erweiterungsmethoden werden statisch aufgelöst. Bei dynamic-Variablen wird der Typ erst zur Laufzeit ermittelt, dadurch können Erweiterungen nicht gefunden werden:
void main() {
dynamic d = '2';
// print(d.parseInt()); // Zur Laufzeit tritt ein NoSuchMethodError auf
var v = '2';
print(v.parseInt()); // Typ wird als String abgeleitet, funktioniert einwandfrei
}
extension NumberParsing on String {
int parseInt() => int.parse(this);
}Code-Sprache: JavaScript (javascript)
Grund: Erweiterungsmethoden werden zur Kompilierzeit anhand des statischen Typs der Variable gebunden, nicht per Suche zur Laufzeit.
Lösungen für API-Namenskonflikte
Es gibt drei Vorgehensweisen, wenn mehrere Erweiterungen Member mit identischem Namen enthalten.
1: Konfliktierende Erweiterungen beim Import mit show / hide ausblenden
// string_apis.dart enthält NumberParsing
// string_apis_2.dart enthält NumberParsing2 mit gleicher Methode parseInt
import 'string_apis.dart';
import 'string_apis_2.dart' hide NumberParsing2;
void main() {
print('42'.parseInt()); // Nutzt die Implementierung aus string_apis.dart
}Code-Sprache: Dart (dart)
2: Expliziter Aufruf der Erweiterung (Wrapper-Syntax)
import 'string_apis.dart'; // NumberParsing
import 'string_apis_2.dart'; // NumberParsing2
void main() {
// print('42'.parseInt()); // Namenskonflikt → Kompilierfehler
print(NumberParsing('42').parseInt());
print(NumberParsing2('42').parseInt());
}Code-Sprache: Dart (dart)
3: Bibliotheken mit Präfix importieren
Verwenden Sie Präfixe zur Unterscheidung zweier Erweiterungen mit identischem Namen:
import 'string_apis.dart';
import 'string_apis_3.dart' as rad;
void main() {
print(NumberParsing('42').parseInt());
print(rad.NumberParsing('42').parseInt());
// Konfliktfreie Erweiterungsmethoden lassen sich weiterhin implizit aufrufen
print('42'.parseNum());
}Code-Sprache: Dart (dart)
Erweiterungsmethoden (Extension methods)