Utilizzo delle classi in Dart
Per creare un’istanza di una classe, ad esempio Person
, si usa l’operatore di assegnamento
usando a destra dell’uguale la chiamata al costruttore opzionalmente preceduta da new
.
Dal momento che new
è opzionale, il compilatore potrebbe segnalare un warning (ad esempio
Dartpad).
Person bob = Person("Bob", "Kennedy");
var alice = Person("Alice", "Brown");
Person john = new Person("John", "Smith");
Si noti, inoltre l’utilizzo di var
per lasciare che sia il compilatore a determinare il tipo
della variabile (in questo caso Person
).
Ovviamente è anche possibile creare riferimenti che siano nullable
Person? bob = Person("Bob", "Kennedy");
Attenzione
Come in Java, anche in Dart tutte le classi sono sottoclassi dirette o indirette di Object
. L’unica
eccezione è la classe Null
che, come visto nell’introduzione a Dart,
è il tipo che indica il valore null
.
L’accesso ai membri (campi e metodi) di una classe, avviene con la “usuale” sintassi basato
sull’operatore .
(dot).
print(bob.firstName); /* Bob */
print(john.secondName); /* Smith */
print(alice.toString()); /* Instance of 'Person' */
Definizione di classi in Dart
In Dart una classe viene definita con la parola chiave class
seguita
dal nome della classe e dal corpo racchiuso tra grafe
class Person {
// corpo della classe
}
Il corpo può contenere:
- variabili, chiamati in questo caso campi della classi e
- funzioni, chiamate in questo caso metodi della classe.
class Person {
String firstName;
String secondName;
String introduce() {
return 'Hello I\'m $firstName $secondName!';
}
}
Costruttori
Il costruttore di una classe è un metodo particolare che ha il compito di
inizializzare tutti i campi della classe. In Dart ci sono diversi tipi di
costruttori:
- unnamed
- named
- costanti
- factory.
Inoltre Dart prevede una sintassi semplificata per operazioni che vengono
svolte spesso nei costruttori.
Attenzione: costruttore e non-nullable
Le variabili non-nullable devono essere inizializzate anche se sono campi di
una classe, il modo più comune di inizializzarle è nel costruttore. Dal momento
che Dart non possiede un valore di default delle variabili non-nullable, spesso
è necessario creare almeno un costruttore per tale operazione. Questa soluzione
è sicuramente preferibile rispetto a quella che dichiara variabili nullable,
questo infatti andrebbe sempre evitato a meno che non sia strettamente necessario.
Costruttore senza nome (unnamed)
Ogni classe Dart ha esattamente un costruttore senza name (unnamed constructor),
il costruttore senza nome è un metodo il cui nome è quello della classe
class Persona {
Person() {
// body of the named constructor
}
}
Il costruttore viene invocato quando si istanzia una variabile
Dart non supporta l’overloading di funzioni e metodi (non possono esistere funzioni
o metodi con lo stesso nome e con una diversa lista dei parametri). Questa regola
vale anche per il costruttore, di conseguenza può esistere un solo costruttore senza
nome. Per avere diversi costruttori, in Dart si usa il concetto di costruttore con
nome.
Costruttore con nomi (named)
Un costruttore con nome è un costruttore a cui viene associato un nome
class Person {
Person.johnSmith() {
// body of the constructor named johnSmith
}
}
l’invocazione del costruttore con nome avviene durante l’istanziazione di una variabile
Person john = Person.johnSmith();
Ovviamente è possibile avere diversi costruttore con nomi diversi, ma non due o più
costruttori con lo stesso nome
class Person {
Person.johnSmith() { } // Ok
Person.aliceBrown() { } // Ok
Person.johnSmith() { } // Errore
}
Costruttori costanti (const)
Costruttori generativi (factory)
Inizializzazione delle variabili d’istanza
Dart impone di inizializzazione tutte le variabili di istanza (campi) che non sono
nullable (alle variabili nullable viene assegnato il valore di default null
, se non
inizializzate). Questa inizializzazione deve avvenire prima di eseguire il corpo del
costruttore, per fare questo si usa una lista di inizializzazione (initializing
list) la cui sintassi ricorda quella del linguaggio C++.
class Person {
String firsName;
String secondName;
Person(String firstName, String secondName) : firstName = firstNane, secondName = secondName {
// corpo del costruttore
}
}
Notiamo che:
- l’initializing list avviene prima dell’esecuzione del corpo;
- ci possono essere diverse inizializzazioni
sinistro = destro
separate da virgola (no punto e virgola); - in
sinistro = destro
non si può usare this
in destro
; - usare
this
in sinistro
è superfluo e quindi è suggerito che venga omesso;
Spesso succede che il costruttore di una classe sia vuoto, non dovendo inizializzare i campi
già inizializzati con l’initializing list. In questo caso (corpo vuoto) Dart prevede una
sintassi particolare che sostituisce il corpo vuoto {}
con un punto e virgola ;
(per
facilità di lettura, nell’esempio sotto l’initializing list è stata indentata su più righe).
class Person {
String firsName;
String secondName;
Person(String firstName, String secondName) :
firstName = firstNane,
secondName = secondName;
}
Per rendere ancora più compatta la sintassi di un costruttore che esegui solamente
l’inizializzazione dei campi, Dart prevede la possibilità di usare initializing
formal parameters, ovvero i parametri formali del costruttore non hanno un nome,
ma vengono usati per inizializzare i campi.
class Person {
String firsName;
String secondName;
Person(this.firstName, this.secondName);
}
Il codice va letto nel seguente modo usa il primo parametro del costruttore
senza nome per inizializzare la variabile di istanza firstName
ed usa il secondo
parametro del costruttore per inizializzare la variabile di istanza secondName
.
Visibilità
Dart non prevede le parole chiavi public
e private
come molti linguaggi ad oggetti, per
creare un membro la cui visibilità sia limitata si deve dare al membro un nome che inizi con
un underscore _
. Tuttavia la visibilità così ottenuta, non è l’equivalente di private
,
ma è un limite imposto a livello di libreria (qualcosa simile alla visibilità package
in
Java), per questo motivo la visibilità implicata da _
viene detta library private.
class Person {
// the following is 'library private'
String _birthDate;
}
Ai fini pratici, la visibilità library private viene usata raramente e solo per quei campi
e metodo di “appoggio”, il motivo di questo è che una variabile di istanza dichiarata non
private (senza underscore) genera implicitamente un getter ed un setter che possono essere
ridefiniti secondo necessità. In altre parole, le variabili non private sono in realtà delle
proprietà e, di conseguenza, il private è riservato ai campi che non sono proprietà che
sono usati meno spesso.
Proprietà
Link utili