Menu
Coddy logo textTech

Classes génériques

Fait partie de la section Programmation Orientée Objet du Journey Dart de Coddy — leçon 65 sur 110.

Vous avez vu comment les collections intégrées de Dart comme List<String> utilisent des paramètres de type. Il est maintenant temps de créer vos propres classes génériques qui fonctionnent avec n'importe quel type que vous spécifiez.

Une classe générique utilise un paramètre de type (généralement T) comme espace réservé pour le type réel qui sera fourni lors de la création d'une instance :

class Box<T> {
  T content;
  
  Box(this.content);
  
  T getContent() => content;
}

void main() {
  var stringBox = Box<String>('Hello');
  var intBox = Box<int>(42);
  
  print(stringBox.getContent());  // Hello
  print(intBox.getContent());     // 42
}

Le T agit comme une variable de type - lorsque vous créez Box<String>, chaque T dans la classe devient String. Cela vous offre une sécurité de type sans avoir à écrire des classes distinctes pour chaque type.

Vous pouvez utiliser plusieurs paramètres de type lorsque votre classe doit fonctionner avec plus d'un type :

class Pair<K, V> {
  K first;
  V second;
  
  Pair(this.first, this.second);
}

void main() {
  var pair = Pair<String, int>('age', 25);
  print('${pair.first}: ${pair.second}');  // age: 25
}

Les classes génériques sont le fondement d'un code réutilisable et sûr au niveau des types. Au lieu de dupliquer la logique pour différents types ou de perdre les informations de type avec dynamic, les génériques vous permettent d'écrire un code flexible que le compilateur peut toujours vérifier.

challenge icon

Défi

Facile

Construisons un système d'emballage de cadeaux qui utilise des classes génériques pour créer des conteneurs flexibles pour différents types d'articles. Vous allez créer une classe générique Wrapper capable d'envelopper n'importe quel type de contenu, et une classe GiftBox avec deux paramètres de type pour associer un cadeau à un message de carte.

Vous organiserez votre code dans deux fichiers :

  • wrapper.dart : Définissez vos classes génériques ici :
    • Une classe générique Wrapper<T> avec une propriété T content et un constructeur. Incluez une méthode unwrap() qui retourne le contenu, et une méthode describe() qui affiche Wrapped: [content]
    • Une classe générique GiftBox<G, C> avec deux propriétés : G gift et C card. Incluez un constructeur et une méthode open() qui affiche Gift: [gift] sur une ligne et Card: [card] sur la ligne suivante
  • main.dart : Importez votre fichier wrapper et faites la démonstration des classes génériques avec différents types :
    • Créez un Wrapper<String> contenant 'Chocolate' et appelez describe()
    • Créez un Wrapper<int> contenant 42 et appelez describe()
    • Créez un Wrapper<double> contenant 3.14 et appelez describe()
    • Affichez une ligne vide
    • Créez un GiftBox<String, String> avec le cadeau 'Teddy Bear' et la carte 'Happy Birthday!', puis appelez open()
    • Affichez une ligne vide
    • Créez un GiftBox<int, String> avec le cadeau 100 (représentant le montant d'une carte-cadeau) et la carte 'Congratulations!', puis appelez open()

Remarquez comment la même classe Wrapper fonctionne parfaitement avec des chaînes de caractères, des entiers et des doubles. La classe GiftBox démontre l'utilisation de deux paramètres de type pour associer différents types ensemble - le cadeau peut être de n'importe quel type tandis que le message de la carte reste une chaîne de caractères.

Sortie attendue :

Wrapped: Chocolate
Wrapped: 42
Wrapped: 3.14

Gift: Teddy Bear
Card: Happy Birthday!

Gift: 100
Card: Congratulations!

Aide-mémoire

Les classes génériques utilisent des paramètres de type (comme T) comme substituts pour les types réels spécifiés lors de la création des instances :

class Box<T> {
  T content;
  
  Box(this.content);
  
  T getContent() => content;
}

void main() {
  var stringBox = Box<String>('Hello');
  var intBox = Box<int>(42);
  
  print(stringBox.getContent());  // Hello
  print(intBox.getContent());     // 42
}

Lorsque vous créez Box<String>, chaque T dans la classe devient String, offrant une sécurité de type sans avoir besoin de classes distinctes pour chaque type.

Utilisez plusieurs paramètres de type lorsque vous travaillez avec plus d'un type :

class Pair<K, V> {
  K first;
  V second;
  
  Pair(this.first, this.second);
}

void main() {
  var pair = Pair<String, int>('age', 25);
  print('${pair.first}: ${pair.second}');  // age: 25
}

Essayez vous-même

import 'wrapper.dart';

void main() {
  // TODO: Créer un Wrapper<String> contenant 'Chocolate' et appeler describe()
  
  // TODO: Créer un Wrapper<int> contenant 42 et appeler describe()
  
  // TODO: Créer un Wrapper<double> contenant 3.14 et appeler describe()
  
  // TODO: Afficher une ligne vide
  
  // TODO: Créer un GiftBox<String, String> avec le cadeau 'Teddy Bear' 
  // et la carte 'Happy Birthday!', puis appeler open()
  
  // TODO: Afficher une ligne vide
  
  // TODO: Créer un GiftBox<int, String> avec le cadeau 100 
  // et la carte 'Congratulations!', puis appeler open()
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet