Constructeurs en Dart

Un constructeur est une méthode particulière qui a pour rôle d'initialiser une instance d'une classe. Ce mécanisme est automatiquement appelé chaque fois qu'un objet est créé. Tous les objets possèdent un constructeur par défaut (qu'il soit déclaré ou non), mais certains langages de programmation permettent la création de logiques de constructeur personnalisées qui influenceront le comportement de l'objet lors de son instanciation.

Dans le langage Dart, il existe six types de constructeurs :

  1. Constructeurs par défaut
  2. Constructeurs génératifs
  3. Constructeurs nommés
  4. Constructeurs de redirection
  5. Constructeurs constants
  6. Constructeurs de fabrique

Constructeurs par défaut

Si aucun constructeur n'est déclaré, Dart va automatiquement générer un constructeur par défaut. Celui-ci ne prend aucun argument et sa seule fonction est d'instancier l'objet.

Constructeurs génératifs

C’est le type de constructeur le plus basique en Dart. Il se déclare avec le même nom que sa classe. Les constructeurs génératifs peuvent accepter des paramètres formels d'initialisation qui peuvent instancier des variables d'instance :

class Pixel {
  int a = 0;
  int b = 0;

  // Le constructeur génératif est appelé avec le nom de la classe, et les paramètres d'initialisation sont passés avec le mot-clé this :
  Pixel(this.a, this.b);
}

Constructeurs nommés

Lorsqu'une classe nécessite plusieurs constructeurs, il est possible d'utiliser des constructeurs nommés pour déclarer différents comportements lors de l'instanciation :

const int aOrigin = 0;
const int bOrigin = 0;

class Pixel {
  int a = 0;
  int b = 0;

  // Constructeur génératif
  Pixel(this.a, this.b);

  // Constructeur nommé
  Point.setOrigin() : a = aOrigin, b = bOrigin;
}

Constructeurs de redirection

Un constructeur de redirection ne possède pas de fonctionnalités propres. Son unique objectif est d’appeler un autre constructeur :

class Pixel {
  int a = 0;
  int b = 0;

  // Constructeur principal
  Pixel(this.a, this.b);

  // Constructeur de redirection
  Pixel.setA(int a) : this(a, 0);
}

Constructeurs constants

Les constructeurs constants génèrent des objets constants à la compilation. Pour cela, il suffit de déclarer un constructeur const et d'utiliser des variables finales :

class ImmutablePixel {
  final int a, b;
  const ImmutablePixel(this.a, this.b);
}

Constructeurs de fabrique

Un constructeur de fabrique peut initialiser différentes versions d’une classe. Par exemple, ces constructeurs peuvent retourner une instance depuis un cache, au lieu de créer une nouvelle instance. Ils peuvent également retourner une instance d’un sous-type :

class Pixel {
  factory Pixel() {
    // Code principal du constructeur de fabrique
  }

  factory Pixel.pixelFromCache() {
    // Code du constructeur de fabrique nommé
  }
}
Remarque : Les constructeurs de fabrique nont pas accès au mot-clé this.

Les constructeurs ne sont pas héritables en Dart

Une sous-classe n'hérite que du constructeur par défaut de sa superclasse. Cependant, un constructeur non par défaut d'une superclasse peut être invoqué dans une sous-classe :

class Pixel {
  String? color;
  Pixel.generate(this.color) {
    print(color);
  }
}

class GreenPixel extends Pixel {
  GreenPixel.generate(super.color) : super.generate();
}

void main() {
  var testPixel1 = Pixel.generate('white');
  var testPixel2 = GreenPixel.generate('green');
}

Paramètres super et liste d'initialisation

Pour mieux gérer les paramètres de constructeur et les superclasses, Dart propose deux outils principaux : les paramètres super et la liste d'initialisation. Les paramètres super permettent de transmettre des paramètres au constructeur de la superclasse. Ils évitent de devoir passer manuellement chaque paramètre dans l'invocation super d’un constructeur :

class Pixel {
  final int a;
  final int b;
  Pixel(this.a, this.b);
}

class Vexel extends Pixel {
  final int c;
  Vexel(super.a, super.b, this.c);
}

Alternativement, la liste d'initialisation peut initialiser des variables d'instance avant l'exécution d'un constructeur de superclasse. Ces listes sont utiles pour la configuration des champs finals :

class Sum {
  final int a;
  final int b;
  final int sumOfParameters;
  Sum(int a, int b) : a = a, b = b, sumOfParameters = a + b;
}

void main() {
  var s = Sum(2, 3);
  print(s.sumOfParameters);
}