Super et this en Dart
Super et this en Dart
Objectifs du cours
- Comprendre le rôle de
superet this dans la programmation orientée objet en Dart. - Savoir utiliser
superpour accéder aux membres de la classe parente. - Savoir utiliser this pour faire référence à l’objet actuel.
- Appliquer ces concepts dans des exemples pratiques et des exercices.
-
Définitions
-
super super: Un mot-clé utilisé pour accéder aux membres (attributs et méthodes) de la classe parente ou de la superclasse.- Le mot-clé
superdans Dart est utilisé pour faire référence aux méthodes ou aux variables de l’objet de classe parent. En termes simples, il est utilisé pour faire référence aux propriétés et méthodes de la superclasse . - L’utilisation la plus importante du mot-clé
superest de supprimer l’ambiguïté entre la superclasse et la sous-classe qui ont des méthodes et des variables portant le même nom. - Le mot-clé
superest capable d’invoquer la méthode et les champs des objets parent, car lorsque nous créons une instance de la sous-classe dans Dart, une instance de la classe parent est également créée implicitement. -
Accéder à la variable de classe parent
-
Accéder aux méthodes de la classe parent
- En Dart, pour accéder aux méthodes de la classe parent, vous pouvez utiliser le mot-clé super. Le mot-clé super est utilisé pour se référer à la classe parent et accéder à ses propriétés et méthodes.
- Voici comment vous pouvez l’utiliser :
- Dans cet exemple, la classe Enfant hérite de la classe Parent. Lorsque vous appelez
super.maMethode()à l’intérieur de la méthodemaMethode()de la classe enfant, cela appelle la méthodemaMethode()de la classe parent. Ensuite, le reste du code de la méthode de la classe enfant est exécuté. -
This
- this : Un mot-clé utilisé pour faire référence à l’objet actuel.
- exemple_this.dart
-
Exemple complet
class Animal {
int count = 150;
}
class Dog extends Animal {
int count = 70;
void printNumber(){
print("Afficher le contenu de la variable locale count: $count");
print("Afficher le contenu de la variable de la classe Animal count:${super.count}");
}
}
void main(){
Dog obj= Dog();
obj.printNumber();
}
//Output
//Afficher le contenu de la variable locale count: 70
//Afficher le contenu de la variable de la classe Animal count:150
Dans l’exemple ci-dessus, nous avons deux classes Animal et Dog , où Animal est la classe
parent(ouSuperclass) et Dog est laclasse enfant(ousous-classe). Il convient de noter que la variable nommée count est déclarée à la fois dans la superclasse et la sous-classe, et lorsque nous utilisonssuper.countelle fera référence à la classe parent (superclasse).
class Parent {
void maMethode() {
print("Méthode de la classe parent");
}
}
class Enfant extends Parent {
void maMethode() {
super.maMethode(); // Appel de la méthode de la classe parent
print("Méthode de la classe enfant");
}
}
void main() {
Enfant enfant = new Enfant();
enfant.maMethode();
}
// exemple_this.dart
// Démonstration complète du mot-clé `this` en Dart
// `this` : référence à l'objet (instance) courant
class CompteBancaire {
String titulaire;
double solde;
static String nomBanque = "Banque Centrale"; // Propriété de classe (pas d'instance)
// 🔹 1. INITIALISATION RACCOURCIE (Initialising Formals)
// `this.` devant le paramètre = assignation automatique à la propriété
CompteBancaire(this.titulaire, [this.solde = 0.0]);
// 🔹 2. DÉSAMBIGUÏSATION (Paramètre vs Propriété)
void deposer(double solde) {
// `this.solde` = propriété de l'objet
// `solde` = paramètre de la méthode
this.solde += solde;
print("💳 Dépôt de ${solde}€. Solde : ${this.solde}€");
}
// 🔹 3. CHAÎNAGE DE MÉTHODES (Method Chaining)
// Retourner `this` permet d'appeler plusieurs méthodes à la suite
CompteBancaire retirer(double montant) {
if (montant <= this.solde) {
this.solde -= montant;
print("💸 Retrait de ${montant}€ réussi.");
} else {
print("❌ Fond insuffisants pour retirer ${montant}€.");
}
return this; // 🌟 Retourne l'instance courante
}
// 🔹 4. PASSER L'INSTANCE COURANTE COMME ARGUMENT
void transfererVers(CompteBancaire destinataire, double montant) {
if (this.solde >= montant) {
this.solde -= montant;
destinataire.solde += montant;
print("🔄 Transfert de ${montant}€ vers ${destinataire.titulaire}");
} else {
print("❌ Transfert impossible : solde insuffisant");
}
}
// 🔹 5. `this` INTERDIT dans un contexte STATIQUE
static void afficherBanque() {
// print(this.nomBanque); // ❌ ERREUR DE COMPILATION : `this` n'existe pas ici
print("🏦 Banque : ${nomBanque}"); // ✅ Accès direct à la propriété static
}
// 🔹 6. EXPLICITE vs IMPLICITE
void afficherInfo() {
// `this.` est optionnel quand il n'y a pas de conflit de nom
print("👤 ${titulaire} | 💰 ${solde}€");
// Equivalent à : print("👤 ${this.titulaire} | 💰 ${this.solde}€");
}
}
// 🔹 BONUS : Constructeur redirecteur utilisant `this`
class Personne {
String nom;
int age;
Personne(this.nom, this.age);
// Ce constructeur appelle automatiquement le constructeur principal
Personne.anonyme() : this("Inconnu", 0);
void sePresenter() => print("Je suis ${this.nom}, ${this.age} ans.");
}
void main() {
print("📘 === Exemples d'utilisation de `this` en Dart ===\n");
// 1️⃣ Initialisation & Désambiguïsation
print("🔹 1. Initialisation & désambiguïsation");
var compte1 = CompteBancaire("Alice", 1000.0);
compte1.deposer(200.0); // Le paramètre `solde` masque la propriété, `this.solde` résout le conflit
// 2️⃣ Chaînage de méthodes (retour de `this`)
print("\n🔹 2. Chaînage de méthodes");
compte1
.retirer(300)
.deposer(50)
.retirer(100);
// 3️⃣ Passer `this` à une autre instance
print("\n🔹 3. Transfert entre comptes (`this` comme référence)");
var compte2 = CompteBancaire("Bob", 500.0);
compte1.transfererVers(compte2, 150.0);
// 4️⃣ Contexte statique (`this` interdit)
print("\n🔹 4. Méthode statique (pas de `this`)");
CompteBancaire.afficherBanque();
// 5️⃣ Explicite vs Implicite
print("\n🔹 5. Accès explicite vs implicite");
compte1.afficherInfo();
// 🎁 Bonus : Constructeur redirecteur
print("\n🎁 Bonus : Constructeur avec `this`");
var inconnu = Personne.anonyme();
inconnu.sePresenter();
}
📄 Fichier 1 : parent.dart (Classe de base)
// parent.dart
// Définition de la classe parent avec méthodes et propriétés
class Parent {
String nom;
int age;
// Constructeur de la classe parent
Parent(this.nom, this.age);
// Méthode à hériter
void sePresenter() {
print("Je suis ${nom}, j'ai ${age} ans. (Parent)");
}
// Méthode utilitaire
String getInfo() {
return "${nom} - ${age} ans";
}
// Méthode virtuelle (à override)
void action() {
print("Action générique du parent");
}
}
📄 Fichier 2 : child.dart (Héritage avec super)
// child.dart
// Classe enfant qui étend Parent et utilise super
import 'parent.dart';
class Enfant extends Parent {
String ecole;
// Constructeur : appel obligatoire à super() si Parent n'a pas de constructeur sans paramètre
Enfant(String nom, int age, this.ecole) : super(nom, age);
@override
void sePresenter() {
// Appel de la méthode parent avec super
super.sePresenter();
print("Je suis élève à : ${ecole}");
}
@override
void action() {
// On enrichit le comportement du parent
super.action();
print("L'enfant joue et apprend 🎮📚");
}
// Nouvelle méthode utilisant super pour accéder à une propriété parent
void afficherInfoParent() {
print("Info depuis parent : ${super.getInfo()}");
}
}
📄 Fichier 3 : this_example.dart (Usage de this)
// this_example.dart
// Démonstration complète du mot-clé `this`
class Produit {
String nom;
double prix;
static int compteur = 0; // Propriété statique
// Initialising formals : syntaxe raccourcie avec this
Produit(this.nom, this.prix) {
compteur++;
print("Produit créé : ${this.nom}"); // this optionnel ici car pas d'ambiguïté
}
// Méthode avec paramètre portant le même nom qu'une propriété
void appliquerRemonte(double prix) {
// this.prix = propriété, prix = paramètre
this.prix += prix;
print("Nouveau prix de ${this.nom} : ${this.prix}€");
}
// Méthode qui retourne l'instance courante (chaînage)
Produit appliquerRemise(double pourcentage) {
this.prix *= (1 - pourcentage / 100);
return this; // Permet le method chaining
}
// Méthode statique : `this` n'est PAS disponible ici ❌
static void afficherCompteur() {
// print(this.nom); // ❌ Erreur : this interdit dans un contexte statique
print("Nombre de produits créés : ${compteur}");
}
}
// Exemple d'utilisation du chaînage
void demoChaining() {
var p = Produit("Ordinateur", 1000.0)
.appliquerRemise(10) // -10%
.appliquerRemonte(50); // +50€
print("Prix final : ${p.prix}€");
}
📄 Fichier 4 : combined_example.dart (this + super ensemble)
// combined_example.dart
// Combinaison avancée de `this` et `super` dans une même classe
import 'parent.dart';
class Employe extends Parent {
double salaire;
static const double TAUX_COTISATION = 0.15;
// Constructeur avec this pour les propriétés de cette classe, super pour le parent
Employe(String nom, int age, this.salaire) : super(nom, age);
@override
void sePresenter() {
// super : accès au comportement parent
super.sePresenter();
// this : accès aux membres de cette instance
print("${this.nom} gagne ${this.salaire}€/mois");
}
// Méthode utilisant à la fois this et super
double calculerSalaireNet() {
// this.salaire : propriété locale
// super.getInfo() : méthode héritée (juste pour l'exemple)
print("Calcul pour : ${super.getInfo()}");
return this.salaire * (1 - TAUX_COTISATION);
}
// Factory constructor avec validation
factory Employe.withMinimumSalary(String nom, int age, double salaire) {
if (salaire < 1500) {
print("⚠️ Salaire trop bas, ajustement à 1500€");
salaire = 1500;
}
// `this` n'est pas disponible ici (contexte statique du factory)
return Employe(nom, age, salaire);
}
// Méthode de comparaison utilisant this
bool estPlusAgeQue(Employe autre) {
return this.age > autre.age; // this.optionnel mais explicite
}
}
📄 Fichier 5 : main.dart (Point d’entrée et tests)
// main.dart
// Fichier principal pour exécuter tous les exemples
import 'parent.dart';
import 'child.dart';
import 'this_example.dart';
import 'combined_example.dart';
void main() {
print("🔹 === Exemple 1 : Héritage avec super ===");
var enfant = Enfant("Léa", 10, "École Primaire");
enfant.sePresenter();
enfant.afficherInfoParent();
enfant.action();
print("\n🔹 === Exemple 2 : Usage de this ===");
demoChaining();
Produit.afficherCompteur();
print("\n🔹 === Exemple 3 : this + super combinés ===");
var employe = Employe.withMinimumSalary("Thomas", 35, 1400);
employe.sePresenter();
print("Salaire net : ${employe.calculerSalaireNet().toStringAsFixed(2)}€");
var employe2 = Employe("Marie", 28, 2000);
print("${employe.nom} est plus âgé que ${employe2.nom} ? ${employe.estPlusAgeQue(employe2)}");
print("\n🔹 === Exemple 4 : Accès explicite à super ===");
class A {
String valeur = "Classe A";
void afficher() => print(valeur);
}
class B extends A {
String valeur = "Classe B";
void afficher() {
print(this.valeur); // "Classe B"
print(super.valeur); // "Classe A"
super.afficher(); // Appel méthode parent
}
}
B().afficher();
}
Types d’utilisation
superest utilisé dans le contexte de l’héritage pour accéder ou modifier les membres de la classe parente.- this est utilisé pour distinguer les membres d’un objet des paramètres ou des variables locales ayant le même nom.
-
Utilisation de
super -
Activité d’exemple :
- Définissez une classe parente appelée Véhicule avec un attribut vitesseMaximale et une méthode afficherVitesseMaximale() qui affiche la vitesse maximale du véhicule.
- Définissez une classe dérivée appelée Voiture qui hérite de la classe Véhicule. Surchargez la méthode afficherVitesseMaximale() pour afficher également le type de véhicule (voiture).
- Créez une instance de Voiture, appelez la méthode afficherVitesseMaximale() et observez les résultats.
-
Exercice :
- Définissez une classe parente appelée Forme avec un attribut couleur et une méthode afficherCouleur() qui affiche la couleur de la forme.
- Définissez une classe dérivée appelée Cercle qui hérite de la classe Forme. Surchargez la méthode afficherCouleur() pour afficher également le type de forme (cercle).
- Créez une instance de Cercle, appelez la méthode afficherCouleur() et observez les résultats.
-
Utilisation de this
-
Activité d’exemple :
- Définissez une classe Personne avec un attribut nom. Déclarez une méthode afficherNom() qui affiche le nom de la personne.
- Déclarez une autre méthode afficherNomAvecThis() qui utilise this pour afficher le nom de la personne.
- Créez une instance de Personne, appelez les deux méthodes et observez les résultats.
-
Exercice :
- Définissez une classe Produit avec des attributs nom et prix. Déclarez une méthode afficherDetails() qui affiche le nom et le prix du produit.
- Déclarez une autre méthode afficherDetailsAvecThis() qui utilise this pour afficher les détails du produit.
- Créez une instance de Produit, appelez les deux méthodes et observez les résultats.
-
Exercice Pratique : Système de Gestion de Bibliothèque
- Code de départ
- Tâches à réaliser
- Remplacez tous les ??? par le code correct.
- Compilez et exécute
// exercice_this_super.dart
// COMPLETER LES TODO SANS MODIFIER LA STRUCTURE EXISTANTE
class Document {
String titre;
String auteur;
bool disponible;
// TODO 1 : Initialisez les propriétés avec `this` (syntaxe courte Dart)
Document(String titre, String auteur, [bool disponible = true]) {
// À compléter
}
void emprunter() {
if (this.disponible) {
this.disponible = false;
print("📖 '${this.titre}' est maintenant emprunté.");
} else {
print("❌ '${this.titre}' est indisponible.");
}
}
// TODO 2 : Méthode de chaînage. Retournez l'instance courante.
Document reserver() {
print("🔖 Réservation de '${this.titre}' confirmée.");
return ???; // Remplacez ???
}
String description() {
return "'${this.titre}' par ${this.auteur} | ${this.disponible ? '✅ Dispo' : '🔒 Emprunté'}";
}
// TODO 3 : Méthode statique. Pourquoi `this` est-il interdit ici ?
static void afficherRegles() {
// print(this.titre); // Décommentez pour voir l'erreur
print("📜 Règle : Emprunt max 14 jours.");
}
}
class Livre extends Document {
int nbPages;
String genre;
// TODO 4 : Appelez le constructeur parent avec `super`
Livre(String titre, String auteur, this.nbPages, this.genre)
: super(???, ???) { // Passez les bons arguments
}
@override
String description() {
// TODO 5 : Utilisez `super.description()` + infos spécifiques
return ???;
}
}
class Magazine extends Document {
String titre; // ⚠️ Shadowing intentionnel (masque Document.titre)
int numeroEdition;
Magazine(String titreDoc, String auteur, this.numeroEdition, this.titre)
: super(titreDoc, auteur);
void afficherDoubleTitre() {
// TODO 6 : Affichez le titre local et le titre parent
print("📰 Magazine (local) : ${???}");
print("📄 Document (parent) : ${???}");
}
}
void main() {
print("🏛️ === Exercice Bibliothèque ===\n");
// 1️⃣ Test Document
var doc = Document("Dart pour tous", "Jean Dupont");
doc.reserver().emprunter(); // Chaînage
// 2️⃣ Test Livre (héritage + super)
var livre = Livre("L'Apprenti Dart", "Marie Code", 320, "Technique");
print(livre.description());
// 3️⃣ Test Magazine (shadowing + this/super)
var mag = Magazine("MagTech", "Éditeur X", 42, "Dart Monthly");
mag.afficherDoubleTitre();
// 4️⃣ Test statique
Document.afficherRegles();
}
