Das final-Klassen-Schlüsselwort
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 40 von 110.
Manchmal möchte man verhindern, dass eine Klasse erweitert wird. Das Schlüsselwort final verhindert, wenn es auf eine Klasse angewendet wird, dass andere Klassen von ihr erben.
final class DatabaseConnection {
String connectionString;
DatabaseConnection(this.connectionString);
void connect() {
print('Connected to $connectionString');
}
}
// Dies würde einen Kompilierzeitfehler verursachen:
// class CustomConnection extends DatabaseConnection { }Eine Klasse als final zu kennzeichnen ist nützlich, wenn deren Erweiterung ihr beabsichtigtes Verhalten oder ihre Sicherheitsgarantien beeinträchtigen könnte. Zum Beispiel muss eine Klasse, die sensible Ressourcen verwaltet, möglicherweise sicherstellen, dass ihre Methoden nicht auf unerwartete Weise überschrieben werden.
Dart 3 hat auch verwandte Modifikatoren eingeführt: base zwingt Unterklassen dazu, extends (nicht implements) zu verwenden, und sealed beschränkt die Vererbung auf dieselbe Bibliothek. Konzentrieren Sie sich vorerst auf final als einfachste Methode, um eine Klasse vollständig abzusperren.
Verwenden Sie final-Klassen, wenn Sie sicher sind, dass das Klassendesign abgeschlossen ist und Vererbung nur Probleme verursachen würde. Verwenden Sie es jedoch nicht zu oft – die meisten Klassen profitieren davon, für Erweiterungen offen zu bleiben.
Aufgabe
EinfachLassen Sie uns ein sicheres Konfigurationssystem erstellen, das zeigt, wann und warum man verhindern möchte, dass eine Klasse erweitert wird. Sie werden eine finale Klasse erstellen, die Anwendungseinstellungen verwaltet und sicherstellt, dass ihr Verhalten nicht durch Vererbung geändert werden kann.
Sie werden Ihren Code in zwei Dateien organisieren:
config.dart: Definieren Sie hier Ihre Konfigurationsklassen:- Eine
final class AppConfig, die nicht erweitert werden kann. Sie sollte eineString appNameund eineString versionEigenschaft haben, einen Konstruktor, der beide Werte entgegennimmt, und eine Methodedisplay(), die[appName] v[version]ausgibt. - Eine reguläre (nicht-finale)
FeatureKlasse mit einerString nameund einerbool enabledEigenschaft, einem Konstruktor für beide und einer Methodestatus(), die[name]: [enabled ? 'ON' : 'OFF']ausgibt. - Eine
ExperimentalFeatureKlasse, dieFeatureerweitert und eineString warningEigenschaft hinzufügt. Überschreiben Siestatus()mit@override, um zuerst die Version der Elternklasse mitsuper.status()aufzurufen und dannWarning: [warning]auszugeben.
- Eine
main.dart: Importieren Sie Ihre Konfigurationsdatei und demonstrieren Sie den Unterschied zwischen finalen und regulären Klassen:- Erstellen Sie eine
AppConfigmit appName'MyApp'und version'2.0.1', und rufen Sie danndisplay()auf. - Geben Sie eine Leerzeile aus.
- Erstellen Sie ein
Featuremit name'Dark Mode'und enabledtrue, und rufen Sie dannstatus()auf. - Geben Sie eine Leerzeile aus.
- Erstellen Sie ein
ExperimentalFeaturemit name'AI Assistant', enabledfalseund warning'May be unstable', und rufen Sie dannstatus()auf.
- Erstellen Sie eine
Beachten Sie, dass AppConfig als final markiert ist, da sein Konfigurationsverhalten konsistent und sicher bleiben soll – keine Unterklasse sollte in der Lage sein, die Verwaltung der Einstellungen zu ändern. Währenddessen bleibt Feature offen für Erweiterungen, was es ExperimentalFeature ermöglicht, spezialisiertes Verhalten hinzuzufügen.
Erwartete Ausgabe:
MyApp v2.0.1
Dark Mode: ON
AI Assistant: OFF
Warning: May be unstableSpickzettel
Das Schlüsselwort final verhindert, dass eine Klasse erweitert wird:
final class DatabaseConnection {
String connectionString;
DatabaseConnection(this.connectionString);
void connect() {
print('Connected to $connectionString');
}
}
// Dies würde einen Kompilierzeitfehler verursachen:
// class CustomConnection extends DatabaseConnection { }Verwenden Sie final Klassen, wenn deren Erweiterung das beabsichtigte Verhalten oder Sicherheitsgarantien beeinträchtigen könnte. Die meisten Klassen sollten für Erweiterungen offen bleiben, sofern es keinen spezifischen Grund gibt, sie einzuschränken.
Dart 3 bietet außerdem die Modifikatoren base (zwingt Unterklassen dazu, extends zu verwenden) und sealed (beschränkt die Vererbung auf dieselbe Bibliothek) an.
Probier es selbst
import 'config.dart';
void main() {
// TODO: Erstelle eine AppConfig mit appName 'MyApp' und version '2.0.1'
// Rufe dann display() auf
// TODO: Gib eine Leerzeile aus
// TODO: Erstelle ein Feature mit name 'Dark Mode' und enabled true
// Rufe dann status() auf
// TODO: Gib eine Leerzeile aus
// TODO: Erstelle ein ExperimentalFeature mit name 'AI Assistant',
// enabled false und warning 'May be unstable'
// Rufe dann status() auf
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienBibliotheken & ImportsEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenInstanzvariablenKonstruktor-GrundlagenWiederholung – Einfacher Taschenrechner4Null-Sicherheit
Einführung in Null-SicherheitNullable vs. Non-NullableDie ? und ! OperatorenLate-Schlüsselwort & Null-SicherheitNull-aware OperatorenNull-Sicherheit in KlassenZusammenfassung – Benutzerprofil-System7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrfache InterfacesRückblick – Shape Calculator10Collections & Generics
Überblick über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische ConstraintsIterable & IteratorZusammenfassung – Generische Speicherung13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
Standard-KonstruktorBenannte KonstruktorenInitialisierungslistenKonstante KonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenZusammenfassung – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _ Präfix-KonventionSichtbarkeit auf Library-EbeneGetter & Setter im DetailInformation HidingRückblick – Schülerdatensätze8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas on-Schlüsselwort in MixinsMixin vs. VererbungMixin vs. InterfaceZusammenfassung - Tiersystem11Spezielle Methoden
toString() überschreibenhashCode & == überschreibenComparable-Interfacecall()-MethodenoSuchMethod überschreibenZusammenfassung – Eigene Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster3Klasseneigenschaften
Instanz- vs. statische MemberFinal & Const FelderLate-VariablenStatische Methoden & FelderGetter und SetterZusammenfassung – Bankkonto-Manager6Vererbung
Grundlagen der VererbungDas super-SchlüsselwortMethoden überschreibenDie @override-AnnotationDas final-Klassen-SchlüsselwortKonstruktoren & VererbungRückblick – Mitarbeiter-Hierarchie9Polymorphismus
Polymorphismus-GrundlagenPolymorphismus über InterfacesTypprüfung zur LaufzeitDie is & as OperatorenDas covariant SchlüsselwortZusammenfassung – Payment Processor