Le mot-clé final pour les classes
Fait partie de la section Programmation Orientée Objet du Journey Dart de Coddy — leçon 40 sur 110.
Parfois, vous souhaitez empêcher une classe d'être étendue. Le mot-clé final, lorsqu'il est appliqué à une classe, empêche toute autre classe d'en hériter.
final class DatabaseConnection {
String connectionString;
DatabaseConnection(this.connectionString);
void connect() {
print('Connected to $connectionString');
}
}
// Cela provoquerait une erreur de compilation :
// class CustomConnection extends DatabaseConnection { }Marquer une classe comme final est utile lorsque son extension pourrait rompre son comportement prévu ou ses garanties de sécurité. Par exemple, une classe qui gère des ressources sensibles pourrait avoir besoin de s'assurer que ses méthodes ne sont pas redéfinies de manière inattendue.
Dart 3 a également introduit des modificateurs associés : base force les sous-classes à utiliser extends (pas implements), et sealed limite l'héritage à la même bibliothèque. Pour l'instant, concentrez-vous sur final comme le moyen le plus simple de verrouiller complètement une classe.
Utilisez les classes final quand vous êtes certain que la conception de la classe est terminée et que l'héritage ne ferait que causer des problèmes. Cependant, n'en abusez pas - la plupart des classes gagnent à rester ouvertes à l'extension.
Défi
FacileConstruisons un système de configuration sécurisé qui démontre quand et pourquoi vous voudriez empêcher une classe d'être étendue. Vous allez créer une classe finale qui gère les paramètres de l'application, garantissant que son comportement ne peut pas être modifié par héritage.
Vous organiserez votre code en deux fichiers :
config.dart: Définissez vos classes de configuration ici :- Une
final class AppConfigqui ne peut pas être étendue. Elle doit avoir une propriétéString appNameetString version, un constructeur qui prend les deux valeurs, et une méthodedisplay()qui affiche[appName] v[version] - Une classe
Featurerégulière (non finale) avec une propriétéString nameetbool enabled, un constructeur pour les deux, et une méthodestatus()qui affiche[name]: [enabled ? 'ON' : 'OFF'] - Une classe
ExperimentalFeaturequi étendFeatureet ajoute une propriétéString warning. Surchargezstatus()en utilisant@overridepour appeler d'abord la version du parent avecsuper.status(), puis afficherWarning: [warning]
- Une
main.dart: Importez votre fichier de configuration et démontrez la différence entre les classes finales et régulières :- Créez un
AppConfigavec appName'MyApp'et version'2.0.1', puis appelezdisplay() - Affichez une ligne vide
- Créez une
Featureavec name'Dark Mode'et enabledtrue, puis appelezstatus() - Affichez une ligne vide
- Créez une
ExperimentalFeatureavec name'AI Assistant', enabledfalse, et warning'May be unstable', puis appelezstatus()
- Créez un
Remarquez comment AppConfig est marquée comme final parce que son comportement de configuration doit rester cohérent et sécurisé - aucune sous-classe ne devrait pouvoir modifier la façon dont les paramètres sont gérés. Pendant ce temps, Feature reste ouverte à l'extension, permettant à ExperimentalFeature d'ajouter un comportement spécialisé.
Sortie attendue :
MyApp v2.0.1
Dark Mode: ON
AI Assistant: OFF
Warning: May be unstableAide-mémoire
Le mot-clé final empêche une classe d'être étendue :
final class DatabaseConnection {
String connectionString;
DatabaseConnection(this.connectionString);
void connect() {
print('Connected to $connectionString');
}
}
// Cela provoquerait une erreur de compilation :
// class CustomConnection extends DatabaseConnection { }Utilisez des classes final lorsque leur extension pourrait rompre le comportement prévu ou les garanties de sécurité. La plupart des classes devraient rester ouvertes à l'extension, à moins qu'il n'y ait une raison spécifique de les verrouiller.
Dart 3 propose également les modificateurs base (oblige les sous-classes à utiliser extends) et sealed (limite l'héritage à la même bibliothèque).
Essayez vous-même
import 'config.dart';
void main() {
// TODO: Créer un AppConfig avec appName 'MyApp' et version '2.0.1'
// Ensuite, appeler display()
// TODO: Afficher une ligne vide
// TODO: Créer un Feature avec name 'Dark Mode' et enabled true
// Ensuite, appeler status()
// TODO: Afficher une ligne vide
// TODO: Créer un ExperimentalFeature avec name 'AI Assistant',
// enabled false, et warning 'May be unstable'
// Ensuite, appeler status()
}
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
1Les bases de la POO
Fichiers externesBibliothèques et importsIntroduction à la POOClasses vs ObjetsLe mot-clé thisMéthodesVariables d'instanceBases des constructeursRécapitulatif - Calculatrice simple4Null Safety
Introduction à la Null SafetyNullable vs Non-NullableLes opérateurs ? et !Le mot-clé late et la Null SafetyLes opérateurs Null-AwareLa Null Safety dans les classesRécapitulatif - Système de profil utilisateur7Classes abstraites et Interfaces
Classes abstraitesMéthodes abstraitesInterfaces en DartInterfaces implicitesImplémentation vs ExtensionInterfaces multiplesRécapitulatif - Calculateur de formes10Collections et Génériques
Aperçu des List, Set et MapCollections Type-SafeClasses génériquesMéthodes génériquesContraintes de généricitéIterable & IteratorRécapitulatif - Stockage générique2Les constructeurs en Dart
Constructeur par défautConstructeurs nommésListes d'initialisationConstructeurs constantsConstructeurs factoryConstructeurs de redirectionRécapitulatif - Shape Builder5Encapsulation
Membres publics vs privésLa convention du préfixe _Visibilité au niveau de la bibliothèqueApprofondissement des Getters & SettersMasquage d'informationsRécapitulatif - Dossiers d'étudiants8Mixins
Introduction aux MixinsCréation de MixinsUtilisation de plusieurs MixinsLe mot-clé on dans les MixinsMixin vs HéritageMixin vs InterfaceRécapitulatif - Système Animal11Méthodes spéciales
Redéfinition de toString()Redéfinition de hashCode & ==Interface ComparableMéthode call()Redéfinition de noSuchMethodRécapitulatif - Collection personnalisée14Patrons de conception Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron ObservateurPatron Stratégie3Propriétés de classe
Membres d'instance vs statiquesChamps Final & ConstVariables LateMéthodes et champs statiquesGetters et SettersRécapitulatif - Gestionnaire de compte bancaire6Héritage
Bases de l'héritageLe mot-clé superRedéfinition de méthodeL'annotation @overrideLe mot-clé final pour les classesConstructeurs et héritageRécapitulatif - Hiérarchie des employés9Polymorphisme
Bases du polymorphismePolymorphisme via les interfacesVérification de type à l'exécutionLes opérateurs is & asMot-clé covariantRécapitulatif - Processeur de paiement12POO Asynchrone
Futures & async/awaitBases des StreamsStream ControllersConstructeurs asynchronesL'asynchrone dans les méthodes de classeRécapitulatif - Data Fetcher15Patrons de conception Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode ModèlePatron ÉtatPatron CompositePatron Repository