Les méthodes d’extension permettent d’ajouter de nouvelles fonctionnalités à des types existants et des types de bibliothèques tierces sans modifier le code source des classes d’origine. L’autocomplétion de l’IDE propose les méthodes d’extension au même rang que les méthodes natives.
Vous ne pouvez pas modifier les API fournies par des tiers, mais vous aimeriez ajouter des méthodes utilitaires pratiques.
Exemple de besoin : utiliser '42'.parseInt() au lieu de int.parse('42').
Structure syntaxique
extension NomExtension? on TypeCible {
// Méthodes, getters, setters, opérateurs, membres statiques
}Langage du code : JavaScript (javascript)
Exemple
// Mettez tout dans un seul fichier, aucune séparation requise, main s’exécute directement
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)); // Comparaison avec une méthode native de String
}Langage du code : Dart (dart)
Résultat de l’exécution
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
42
3.14
100Langage du code : CSS (css)
Le nom de l’extension est facultatif ; this représente l’instance qui appelle l’extension.
Types de membres pris en charge par les extensions
Dans une extension, vous pouvez définir : méthodes d’instance, getters, setters, opérateurs et membres statiques.
extension StringExt on String {
// getter
bool get isEmptyOrBlank => trim().isEmpty;
// Méthode d’instance
String repeat(int count) => List.filled(count, this).join();
// Surcharge d’opérateur
String operator *(int count) => repeat(count);
// Membre statique (accessible via le nom de l’extension)
static String emptyStr = '';
static void printTip() => print("Méthode statique de l’extension chaîne");
}
void main() {
var str = " dart ";
print(str.isEmptyOrBlank);
print("hi" * 3);
print("hello".repeat(2));
// Accès aux membres statiques
print(StringExt.emptyStr);
StringExt.printTip();
}Langage du code : PHP (php)
Résultat de l’exécution
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
false
hihihi
hellohello
Méthode statique de l’extension chaîneLangage du code : CSS (css)
Extensions sans nom (extensions anonymes)
Si vous ne spécifiez pas de nom d’extension, celle-ci n’est visible qu’au sein de la bibliothèque actuelle. Les conflits ne peuvent pas être résolus par appel explicite.
Il est impossible de l’appeler depuis l’extérieur avec la syntaxe NomExtension(instance).
extension on String {
bool get isBlank => trim().isEmpty;
}
void main() {
print(" ".isBlank); // true
}Langage du code : Dart (dart)
Les membres statiques d’une extension anonyme ne sont utilisables qu’à l’intérieur de cette extension.
Extensions génériques (Generic extensions)
Vous pouvez créer des extensions pour des types génériques, par exemple 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]]
}Langage du code : Dart (dart)
Résultat de l’exécution
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
8
[4, 3, 2, 1]
[[1, 2], [3, 4]]Langage du code : CSS (css)
Type statique static vs dynamic
Les variables dynamic ne peuvent pas appeler les méthodes d’extension
Les méthodes d’extension sont résolues de manière statique. Une variable dynamic dont le type n’est déterminé qu’à l’exécution ne parvient pas à trouver l’extension :
void main() {
dynamic d = '2';
// print(d.parseInt()); // Erreur NoSuchMethodError à l’exécution
var v = '2';
print(v.parseInt()); // Le type est déduit comme String, fonctionne normalement
}
extension NumberParsing on String {
int parseInt() => int.parse(this);
}Langage du code : JavaScript (javascript)
Cause : les méthodes d’extension sont liées au moment de la compilation selon le type statique de la variable, pas par recherche à l’exécution.
Solutions aux conflits de noms d’API
Trois solutions existent lorsque plusieurs extensions possèdent des membres de même nom.
1 : Masquer les extensions conflictuelles avec show / hide lors de l’import
// string_apis.dart contient NumberParsing
// string_apis_2.dart contient NumberParsing2 qui définit aussi parseInt
import 'string_apis.dart';
import 'string_apis_2.dart' hide NumberParsing2;
void main() {
print('42'.parseInt()); // Utilise l’implémentation de string_apis.dart
}Langage du code : Dart (dart)
2 : Appel explicite de l’extension (syntaxe d’encapsulation)
import 'string_apis.dart'; // NumberParsing
import 'string_apis_2.dart'; // NumberParsing2
void main() {
// print('42'.parseInt()); // Conflit, erreur de compilation
print(NumberParsing('42').parseInt());
print(NumberParsing2('42').parseInt());
}Langage du code : Dart (dart)
3 : Importer la bibliothèque avec un préfixe
Utilisez un préfixe pour distinguer deux extensions portant exactement le même nom :
import 'string_apis.dart';
import 'string_apis_3.dart' as rad;
void main() {
print(NumberParsing('42').parseInt());
print(rad.NumberParsing('42').parseInt());
// Les méthodes d’extension sans conflit restent appelables implicitement
print('42'.parseNum());
}Langage du code : Dart (dart)
Méthodes d’extension (Extension methods)