Méthodes d’extension (Extension methods)

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 lextension 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)

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *