La parola chiave final per le classi
Fa parte della sezione Programmazione orientata agli oggetti del percorso Dart di Coddy. Lezione 40 di 110.
A volte vuoi impedire che una classe venga estesa. La parola chiave final, quando viene applicata a una classe, impedisce a qualsiasi altra classe di ereditarla.
final class DatabaseConnection {
String connectionString;
DatabaseConnection(this.connectionString);
void connect() {
print('Connected to $connectionString');
}
}
// Questo causerebbe un errore in fase di compilazione:
// class CustomConnection extends DatabaseConnection { }Contrassegnare una classe come final è utile quando estenderla potrebbe compromettere il comportamento previsto o le garanzie di sicurezza. Per esempio, una classe che gestisce risorse sensibili potrebbe dover garantire che i suoi metodi non vengano sovrascritti in modi imprevisti.
Dart 3 ha introdotto anche modificatori correlati: base obbliga le sottoclassi a usare extends (non implements) e sealed limita l'ereditarietà alla stessa libreria. Per ora, concentrati su final come modo più semplice per rendere una classe completamente immodificabile.
Usa le classi final quando sei certo che la progettazione della classe sia completa e che l'ereditarietà causerebbe solo problemi. Tuttavia, non abusarne: la maggior parte delle classi trae vantaggio dal rimanere aperta all'estensione.
Sfida
FacileCostruiamo un sistema di configurazione sicuro che mostri quando e perché vorresti impedire che una classe venga estesa. Creerai una classe final che gestisce le impostazioni dell'applicazione, assicurandoti che il suo comportamento non possa essere modificato tramite ereditarietà.
Organizzerai il codice in due file:
config.dart: definisci qui le classi di configurazione:- Una
final class AppConfigche non può essere estesa. Dovrebbe avere le proprietàString appNameeString version, un costruttore che accetta entrambi i valori e un metododisplay()che stampa[appName] v[version] - Una classe
Featurenormale (non final) con le proprietàString nameebool enabled, un costruttore che accetta entrambe e un metodostatus()che stampa[name]: [enabled ? 'ON' : 'OFF'] - Una classe
ExperimentalFeatureche estendeFeaturee aggiunge la proprietàString warning. Sovrascrivistatus()usando@overrideper chiamare prima la versione della classe genitore consuper.status(), quindi stampareWarning: [warning]
- Una
main.dart: importa il file di configurazione e mostra la differenza tra le classi final e quelle normali:- Crea un
AppConfigcon appName'MyApp'e version'2.0.1', quindi chiamadisplay() - Stampa una riga vuota
- Crea un
Featurecon name'Dark Mode'ed enabledtrue, quindi chiamastatus() - Stampa una riga vuota
- Crea un
ExperimentalFeaturecon name'AI Assistant', enabledfalsee warning'May be unstable', quindi chiamastatus()
- Crea un
Nota come AppConfig sia contrassegnata come final perché il suo comportamento di configurazione dovrebbe rimanere coerente e sicuro: nessuna sottoclasse dovrebbe poter modificare il modo in cui vengono gestite le impostazioni. Nel frattempo, Feature rimane aperta all'estensione, consentendo a ExperimentalFeature di aggiungere un comportamento specializzato.
Output previsto:
MyApp v2.0.1
Dark Mode: ON
AI Assistant: OFF
Warning: May be unstableProvalo tu
import 'config.dart';
void main() {
// TODO: Crea un AppConfig con appName 'MyApp' e versione '2.0.1'
// Poi chiama display()
// TODO: Stampa una riga vuota
// TODO: Crea una Feature con nome 'Dark Mode' e enabled true
// Poi chiama status()
// TODO: Stampa una riga vuota
// TODO: Crea una ExperimentalFeature con nome 'AI Assistant',
// enabled false e warning 'May be unstable'
// Poi chiama status()
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniLibrerie e importazioniIntroduzione alla programmazione orientata agli oggettiClassi e oggettiLa parola chiave thisMetodiVariabili di istanzaFondamenti dei costruttoriRiepilogo - Calcolatrice semplice4Sicurezza dei valori null
Introduzione alla sicurezza dei valori nullNullable e non nullableGli operatori ? e !La keyword late e la sicurezza dei valori nullOperatori null-awareSicurezza dei valori null nelle classiRiepilogo - Sistema di profili utente7Classi astratte e interfacce
Classi astratteMetodi astrattiInterfacce in DartInterfacce impliciteImplementare vs estendereInterfacce multipleRiepilogo - Calcolatore di forme10Collezioni e generici
Panoramica di List, Set e MapCollezioni sicure rispetto ai tipiClassi genericheMetodi genericiVincoli sui genericiIterable e IteratorRiepilogo - Archiviazione generica13Concetti avanzati di OOP
Composizione vs ereditarietàMetodi di estensioneClassi chiamabiliClassi sealed (Dart 3)Record (Dart 3)Pattern e matching (3.0)Enum con metodi16Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e Utente2I costruttori in Dart
Costruttore predefinitoCostruttori con nomeListe di inizializzazioneCostruttori costantiCostruttori factoryCostruttori di reindirizzamentoRiepilogo - Crea forme5Incapsulamento
Membri pubblici e privatiConvenzione del prefisso _Visibilità a livello di libreriaApprofondimento su getter e setterNascondimento delle informazioniRiepilogo - Registri degli studenti8Mixin
Introduzione ai MixinCreare MixinUsare più MixinParola chiave on nei MixinMixin vs ereditarietàMixin vs interfacciaRiepilogo - Sistema animale11Metodi speciali
Sovrascrittura di toString()Sovrascrittura di hashCode e ==Interfaccia ComparableMetodo call()Sovrascrittura di noSuchMethodRiepilogo - Collezione personalizzata14Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneSingletonFactoryObserverStrategy3Proprietà delle classi
Membri di istanza e staticiCampi final e constVariabili lateMetodi e campi staticiGetter e setterRiepilogo - Gestore di conti bancari6Ereditarietà
Ereditarietà di baseLa parola chiave superOverride dei metodiL’annotazione @overrideLa parola chiave final per le classiCostruttori ed ereditarietàRiepilogo - Gerarchia dei dipendenti9Polimorfismo
Fondamenti di polimorfismoPolimorfismo tramite interfacceControllo del tipo a runtimeGli operatori is e asParola chiave covariantRiepilogo - Elaboratore di pagamenti12OOP asincrona
Future e async/awaitNozioni di base sugli streamController degli streamCostruttori asincroniAsync nei metodi di classeRiepilogo - Recupero dei dati15Pattern di progettazione Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositePattern RepositoryEsercitati da solo: Compilatore Dart online