Ce chapitre vous montre comment utiliser les fonctionnalités majeures de Dart, des variables et opérateurs jusqu’aux classes et bibliothèques, en supposant que vous savez déjà programmer dans un autre langage.
Consultez les spécifications Dart lorsque vous souhaitez en savoir plus sur une des fonctionnalités du langages.
Le programme suivant utilise quelques unes des fonctionnalités de base de Dart :
Voici ce qu’utilise ce programme et qu’utilisent toutes (ou presque toutes) les applications Dart :
// Ceci est un commentaire.
Utilisez // pour indiquer que le reste de la ligne est un commentaire. Vous pouvez également utiliser /* … */. Pour plus de détails, voir Commentaires.
num
Un type. Quelques un des autres types natifs sont String, int, and bool.
42
Un nombre littéral. Les littéraux sont une sorte de constante à la compilation.
print()
Un moyen pratique d’afficher dans la sortie.
'...'
(ou "..."
)Une chaine de caratère.
$nomDeVariable
(ou ${expression}
)L’interpolation: permet l’évaluation de variables ou d’expressions à l’intérieur d’une chaîne de caractères littérale. Pour plus d’informations, Voir Strings.
main()
La fonction spéciale, nécessaire et de premier niveau où l’application démarre. Pour plus d’information, Voir La fonction main().
var
Une façon de déclarer une variable sans avoir à préciser son type.
Pendant que vous apprenez le langage Dart, gardez ces principes et concepts en mémoire :
Tout ce que vous pouvez placer dans une variable est un objet,
et tout objet est une instance d’une classe. Y compris les nombres,
les fonctions et null
sont des objets. Tous les objets héritent
de la classe Object.
Le typage statique (comme num
dans l’exemple précédent) clarifie
votre intention et permet aux outils de détecter les erreurs de
types, mais il reste optionnel. (Pendant que vous debuggez votre
code, vous pourrez remarquer que les variables dont le type n’a pas
été spécifié ont un type spécial : dynamic
.)
Dart analyse tout votre code avant de le lancer. Vous pouvez fournir des indications à Dart, par exemple, en précisant les types ou les constantes de compilation, pour détecter les erreurs ou encore pour accélérer l’exécution de votre code.
Dart supporte les fonctions de premier niveau (tel que main()
),
tout comme elles peuvent être attachées à une classe ou un objet
(respectivement, fonctions statiques et méthodes d’instance).
Vous pouvez aussi créer des fonctions à l’intérieur d’une fonction
(fonctions imbriquées ou fonctions internes).
De la même façon, Dart supporte les variables de premier niveau, tout comme elles peuvent être attachées à une classe ou un objet (variables statiques et variables d’instance). Les variables d’instance sont aussi nommées champs ou propriétés.
Contrairement à Java, Dart n’a pas les mots-clés public
,
protected
et private
. Si un identifiant commence avec un
souligner (_), il est privé à sa librairie. Pour plus de détails,
voir Bibliothèques et visibilité.
Les identifiants peuvent commencer par une lettre ou _, suivi de n’importe quelle combinaison de ces caractères ou chiffres.
Il est parfois important de distinguer expression et instruction, nous allons préciser la différence entre ces deux mots.
Les outils Dart peuvent rapporter deux types de problèmes : des alertes et des erreurs. Les alertes indiquent juste que votre code peut ne pas fonctionner, mais n’empêche pas l’exécution de votre programme. Une erreur de compilation empêche totalement l’exécution de votre programme; une erreur à l’exécution résulte d’une exception qui est remontée lorsque le code s’exécute.
Dart a deux modes d’exécution : production et checked. Nous recommandons de développer et debugger en mode checked, et déployer en mode production.
Le mode production est le mode d’exécution par défaut d’un programme Dart, optimisé pour la vitesse. Le mode production ignore les instructions d’assertion et le typage statique.
Le mode checked est un mode pour les développeurs, qui aide à
détecter certaines erreurs de type à l’exécution. Par exemple, si on
affecte une chaîne de caractères à une variable déclarée comme num
,
alors le mode checked lance une exception.
Le tableau suivant liste les mots clés que le langage Dart traite spécialement.
abstract 1 | continue | false | new | this |
as 1 | default | final | null | throw |
assert | deferred 1 | finally | operator 1 | true |
async 2 | do | for | part 1 | try |
async* 2 | dynamic 1 | get 1 | rethrow | typedef 1 |
await 2 | else | if | return | var |
break | enum | implements 1 | set 1 | void |
case | export 1 | import 1 | static 1 | while |
catch | external 1 | in | super | with |
class | extends | is | switch | yield 2 |
const | factory 1 | library 1 | sync* 2 | yield* 2 |
1 Les mots annotés 1 sont des identifiants primitifs.
Evitez d’utiliser les identifants primitifs en tant qu’identifiant,
et ne les utilisez jamais en nom de classe ou de type.
Les identifiants primitifs existent pour faciliter le portage de JavaScript à Dart.
Par exemple, si du code JavaScript a une variable nommée factory
,
il n’est pas nécessaire de la renommer lorsque vous portez le code en Dart.
2 Les mots annotés 2 sont nouveaux,
ces mots réservés sont liés au support de l’asynchronisme
qui a été ajouté après la sortie de la version 1.0 de Dart.
Vous ne pouvez pas utiliser async, await, ou yield en tant
qu’identifiant dans le corps d’une fonction marquée avec async
, async*
, or sync*
.
Pour plus d’information, voir
Support de l’asynchronisme.
Tous les autres mots de ce tableau sont des mots réservés. Vous ne pouvez pas utiliser les mots réservés en tant qu’identifiant.
Ici un exemple de création de variables et d’affectation de valeur:
Les variables sont des références. La variable name
contient une référence
vers une chaine de caractères qui a pour valeur “Bob”.
Les variables non initialisées ont null
pour valeur initiale, même les
variables numériques car les nombres sont des objets.
Vous avez la possibilité d’assigner un type statique à une variable lors de sa déclaration:
Mettre des types est un bon moyen de spécifier son intention. Les outils comme les compilateurs et éditeurs peuvent utiliser ces types pour vous aider, en fournissant de la l’auto-complétion de code ou des alertes préventives sur des bugs.
Si vous n’avez pas l’intention de changer une variable, utiliser final
ou
const
, plutôt que var
ou en complément à un type. Une variable final peut
seulement être affectée une fois; une variable const peut être une constante de
compilation.
Une variable de premier niveau ou de classe déclarée comme final
est initialisée
à sa première utilisation:
Utilisez const
pour les variables que vous souhaitez utiliser comme constantes de
compilation. Si la variable const est au niveau de la classe, mettez la en
static const
(les variables d’instances ne peuvent pas être const). Là où vous
déclarez la variable, mettez la valeur en constante de compilation comme
un litéral, une variable const, ou le résultat d’une opération arithmétique sur
une constante numérique.
Les types suivants sont nativement supportés par le langage Dart :
Vous pouvez initialiser un objet de n’importe quel de ces types en
utisant une valeur littérale. Par exemple, 'ceci est une chaîne de
caractères'
est une chaîne de caractères littérale, et true
est un
booléen littéral.
Comme toute variable en Dart fait référence à un objet, c’est-à-dire,
à une instance d’une classe, vous pouvez utiliser des constructeurs
pour initialiser ces variables. Certains de ces types natifs ont leurs
propres constructeurs. Par exemple, vous pouvez utiliser le constructeur
Map()
pour créer un dictionnaire, en utilisant le code tel que
new Map()
.
Il existe deux types de nombre en Dart :
int
Les valeurs entières, qui doivent être généralement comprise entre -253 et 253
double
Les nombres à virgule flottante 64-bit (double précision), tel que spécifié dans le standart IEEE 754
Les deux types int
et double
sont des sous-types de
num
. Le type num
inclut des opérateurs de base tel que +, -, /, et *, mais aussi les
opérateurs bit à bit tel que >>. Vous trouverez aussi dans le type
num, abs()
, ceil()
, et floor()
, parmi d’autres méthodes. Si num
et ses sous-types n’ont pas ce que vous recherchez, la librairie
Math peut vous aider.
Les entiers sont des nombres sans décimale. Voici quelques exemples de nombres entiers littéraux :
Si un nombre inclut une décimale, c’est un double. Voici quelques exemples de nombres réels littéraux :
Voici comment transformer une chaîne de caractères en nombre, ou vice versa :
Le type int définit le traditionnel décalage de bit (<<, >>), les opérateurs ET (&), et OU (|). Par exemple :
Une chaîne de caractères en Dart est une suite de codes UTF-16. Vous pouvez utiliser des guillemets simples ou des guillemets doubles pour créer une chaîne de caractères :
Vous pouvez mettre la valeur d’une expression à l’intérieur d’une
chaine de caractères en utilisant ${
expression
}
. Si
l’expression est un identifiant, vous pouvez omettre les {}. Pour
obtenir la chaîne de caractères correspondant à un objet, Dart appelle
la méthode toString()
de l’objet.
Vous pouvez concatener des chaînes de caractères en utilisant des
chaînes littérales adjacentes ou l’opérateur +
:
Une autre façon de créer des chaînes de caractères multiligne est d’utiliser un triplet de guillements qu’ils soient simple ou double :
Vous pouvez créer une chaîne de caractères “brute” en la préfixant
avec r
:
Vous pouvez utiliser l’échappement Unicode dans une chaîne de caractères :
Pour plus d’information sur comment utiliser les chaînes de caractères, voir Chaînes de caractères et expressions régulières.
Pour représenter les valeurs booléennes, Dart a un type nommé bool
.
Seulement deux objets ont le type booléen: les valeurs littérales
true
et false
, respectivement vrai et faux.
Lorsque Dart s’attend à une valeur booléenne, seulement la valeur true
est considérée comme vraie. Toutes les autres valeurs sont considérées
comme fausses. Contrairement à JavaScript, les valeurs telles que 1
,
"uneChaineDeCaractères"
, et unObjet
sont toutes considérées comme
fausses.
Par exemple, considérons le code suivant qui est valide en JavaScript et en Dart :
Si vous lancez ce code en JavaScript, il affiche “Tu as un nom !” car
nom
est un objet non null. Par contre, en Dart tournant dans un mode
production, le code précédent n’affiche rien car nom
est converti
en false
(car nom != true
). En Dart tournant dans un mode de
vérification le code précédent lance une exception car la variable nom
n’est pas un booléen.
Voici un autre exemple de code qui se comporte différement entre JavaScript et Dart :
Le traitement des booléens en Dart a été conçu de façon à éviter des
comportements étranges qui peuvent arriver lorsque de nombreuses valeurs
sont considérées comme vraie. Ce qui signifie pour vous c’est que,
au lieu d’utiliser du code comme
if (valeurNonBooleene)
, il faut plutôt
vérifier explicitement les valeurs. Par exemple :
Peut-être la collection la plus commune dans presque tous les langages de programmation est le tableau, ou un groupe ordonné d’objets. Dans Dart, les tableaux sont des objets List, donc nous les appelons juste listes.
Les listes littérales Dart ressemblent aux tableaux littéraux de JavaScript. Voici un exemple d’une liste Dart :
Les listes utilisent une indexation à partir de zero, c’est-à-dire que
l’index 0 est l’index du premier élément et list.length - 1
est
l’index du dernier élément. Vous pouvez obtenir la longueur de la liste
et faire référence aux éléments de la liste comme vous le feriez en
JavaScript :
Le type List a plein de méthodes pratiques pour manipuler les listes. Pour plus d’information sur les listes, voir les Génériques et les Collections.
En général, un dictionnaire est un objet qui associe des clés et des valeurs. Clés et valeurs peuvent être de n’importe quel type d’objet. Chaque clé n’apparait qu’une seule fois, mais vous pouvez utiliser une même valeur plusieurs fois. Le support des dictionnaires par Dart est fait grâce aux dictionnaires en valeurs littérales et au type Map.
Voici quelques exemples simples de dictionnaire en Dart, créés en utilisant des valeurs littérales :
Vous pouvez créer les mêmes objets en utilisant le constructeur Map :
Ajoutez une nouvelle paire clé-valeur à un dictionnaire existant comme vous le feriez en JavaScript :
Récupérez une valeur d’un dictionnaire de la même façon que vous le feriez en JavaScript :
Si vous cherchez une clé qui n’est pas dans le dictionnaire, vous obtenez un null en retour :
Utilisez .length
pour obtenir le nombre de paires clé-valeur dans le
dictionnaire :
Pour plus d’information sur les dictionnaires, voir les Génériques et Maps.
Un objet Symbol représente un opérateur ou un identifiant déclaré dans un programme Dart. Vous n’aurait peut être jamais le besoin d’utiliser les symboles, mais ils sont indispensables pour les APIs qui font référence à un identifiant par son nom, car la minification change les noms des identifiants mais pas ceux des symboles.
Pour obtenir le symbole pour un identifiant, utilisez un symbole
littéral, qui est juste un #
suivi de l’identifiant :
Pour plus d’information sur les symboles, voir dart:mirrors - reflection.
Voici un exemple d’implémentation d’une fonction:
Bien que les conventions recommandent de spécifier les types de paramètre et de retour, ce n’est pas obligatoire :
Pour les fonctions qui contiennent seulement une expression, vous pouvez utiliser la syntaxe abrégée:
La syntaxe => expr;
est un raccourci pour
{ return expr;}
. Dans la fonction afficherNombre()
,
l’expression est l’appel à la fonction de premier niveau print()
.
Voici un exemple d’appel à une fonction:
Une fonction peut avoir deux types de paramètres: requis ou optionnel. Les paramètres requis sont placés en premier, suivi par les paramètres optionnels.
Les paramètres optionnels peuvent être soit positionné ou nommé, mais pas les deux en même temps.
Les deux peuvent avoir une valeur par défaut. Elle doit être une constante de
compilation telle qu’une valeur littérale. Si aucune valeur par défaut n’est
fournie, la valeur est null
.
Lors de l’appel à une fonction, vous pouvez spécifier un paramètre positionnés ou nommés par
nomParametre: valeur
. Par exemple:
Lors de la définition d’une fonction:
{param1, param2, …}
Pour spéficier les paramètres nommés:
Utilisez deux points (:
) pour indiquer la valeur par défaut:
Placer un ensemble de paramètres de fonctions entre des []
les indiquent
comme des paramètres optionnels positionnés :
Voici un exemple d’appel à cette fonction sans paramètre optionnel :
Et ici, un exemple d’appel à cette fonction avec un troisième paramètre:
Utilisez =
pour indiquer la valeur par défaut :
Toute application doit avoir une fonction de premier niveau main()
qui
sert de point d’entrée. Elle retourne void
et a un paramètre optionnel
List<String>
comme argument.
Voici un exemple de la fonction main()
pour une application web:
Voici un exemple de la fonction main()
pour une application en ligne de
commandes qui prend des arguments :
Vous pouvez utiliser la bibliothèque args pour définir et analyser des lignes de commandes.
Vous pouvez passer une fonction comme paramètre à une autre fonction. Par exemple :
Vous pouvez aussi affecter une fonction à une variable comme ceci :
Dart est un langage à portée lexicale, ce qui signifie que la portée d’une variable est déterminée statiquement, simplement par la mise en page du code. Vous pouvez “scruter les accolades limitrophes” pour voir si une variable est dans la portée.
Voici un exemple de test d’égalité sur des fonctions de premier niveau, des méthodes statiques, et des méthodes d’instance :
Notez comment fonctionImbriquee()
peut utiliser des variables de chaque niveau,
, en remontant jusqu’au premier niveau.
Une closure est un objet fonction qui a un accès aux variables de sa portée lexicale, même si la fonction est utilisée en dehors de sa portée d’origine.
Les fonctions peuvent se refermer sur les variables définies dans des portées
environnantes. Dans l’exemple suivant, creeAdditionneur()
capture la variable ajouterPar
.
Partout où la fonction retounée est utilisée, il rapelle ajouterPar
.
Voici un exemple de test d’égalité de fonctions de premier niveau, de méthodes statiques et de méthodes d’instance.
Toutes les fonctions retournent une valeur. Si aucune valeur n’est retournée,
l’instruction return null;
est implicitement ajoutée au corps de la fonction.
Les opérateurs définis par Dart sont présentés dans le tableau suivant. Vous pouvez surcharger certains de ces opérateurs, comme décrit dans Surcharge d’opérateurs.
Description | Opérateur |
---|---|
post-fixé unaire | expr++ expr-- () [] . |
pre-fixé unaire | -expr !expr ~expr ++expr --expr |
multificatif | * / % ~/ |
additif | + - |
décalage | << >> |
ET binaire | & |
OU exclusif binaire | ^ |
OU binaire | | |
comparateur et test de type | >= > <= < as is is! |
égalité | == != |
ET logique | && |
OU logique | || |
conditionnel | expr1 ? expr2 : expr3 |
cascade | .. |
assignation | = *= /= ~/= %= += -= <<= >>= &= ^= |= |
Quand vous utilisez les opérateurs, vous créez des expressions. Voici quelques exemples d’expressions utilisant des opérateurs :
Dans le précédent tableau des opérateurs, chaque opérateur a une plus haute priorité que les opérateurs dans des lignes suivantes.
Par exemple, l’opérateur multiplicatif %
a une priorité plus importante que (et de ce fait s’éxecute avant) l’opérateur ==
, qui a lui même une priorité plus importante que l’opérateur logique AND &&
.
Cette priorié signifie que les deux lignes de code suivantes s’exécutent de la même façon :
Dart supporte les opérateurs arithmétiques usuels, comme montré dans le tableau suivant.
Operateur | Signification |
---|---|
+ |
Ajoute |
– |
Soustrait |
-expr |
Moins unaire, aussi connu comme négation (inverse le signe de l’expression) |
* |
Multiplie |
/ |
Divise |
~/ |
Divise, retourne un résultat entier |
% |
Donne le reste d’une division entière (modulo) |
Example:
Dart supporte également les opérateurs de pré et post incrémentation et décrémentation
Opérateur | Signification |
---|---|
++var |
var = var + 1 (la valeur de l’expression est var + 1 ) |
var++ |
var = var + 1 (la valeur de l’expression est var ) |
--var |
var = var – 1 (la valeur de l’expression est var – 1 ) |
var-- |
var = var – 1 (la valeur de l’expression est var ) |
Exemple:
Le tableau suivant liste les significations des opérateurs d’égalité et de comparaison.
Opérateur | Signification |
---|---|
== |
Egal; voir plus bas |
!= |
Différent |
> |
Plus grand que |
< |
Plus petit que |
>= |
Plus grand ou égal |
<= |
Plus petit ou égal |
Pour tester si deux objets x et y sont identiques, utilisez l’opérateur ==
. (Dans les rares cas où vous avez besoin de savoir si deux objets sont éxactement le même, utilisez la fonction
identical() à la place.) Voici comment fonctionne l’opérateur ==
:
Si x ou y est nul, renvoie vrai si les deux sont nuls, et faux si seulement l’un d’eux est nul.
Retourne le résultat de l’invocation
x.==(y)
. (C’est ça, les opérateurs tels que ==
sont des méthodes invoquées sur leur premier opérande. Vous pouvez surcharger grand nombre de ces opérateurs, y compris ==
, comme vous pouvez le voir dans
Surcharge d’opérateurs.)
Voilà un exemple d’utilisation de chacun des opérateurs d’égalité et de comparaison :
Les opérateurs as
, is
, and is!
sont très utiles pour tester les types durant l’éxecution.
Opérateur | Signification |
---|---|
as |
Conversion de type |
is |
Vrai si l’objet a le même type |
is! |
Faux si l’objet n’a pas le même type |
Le résultat de obj is T
est vrai si obj
implémente l’interface précisée par T
. Par exemple, obj is Object
est toujours vrai.
Utilisez l’opérateur as
pour convertir un objet dans un type spécifique. En général, vous devez l’utiliser comme raccourci pour un test is
sur un objet suivi d’une expression utilisant cet objet. Par exemple, dans le code suivant :
Vous pouvez simplifier le code en utilisant l’opérateur as
:
Comme nous l’avons déjà vu, vous pouvez assigner des valeurs en utilisant l’opérateur =
.
Vous pouvez également utiliser des opérateurs composés tels que +=
, qui combine une opération
avec une assignation.
= |
–= |
/= |
%= |
>>= |
^= |
+= |
*= |
~/= |
<<= |
&= |
|= |
Voici comment fonctionnent les opérateurs d’assignation :
Assignation composée | Expression équivalente | |
---|---|---|
Pour un opérateur op: | a op= b |
a = a op b |
Exemple: | a += b |
a = a + b |
L’exemple suivant utilise à la fois une assignation et une assignation composeé :
Vous pouvez inverser ou combiner des expressions booléenes en utilisant des opérateurs logiques.
Operateur | Signification |
---|---|
!expr |
Inverse l’expression qui le suit (change faux en vrai, et vice versa) |
|| |
OU logique |
&& |
ET logique |
Voici un exemple d’utilisation des opérateurs logiques :
Vous pouvez manipuler individuellement les bits des nombres en Dart. Généralement, vous utiliserez ces opérateurs binaires et de décalage de bits avec des entiers.
Opérateur | Signification |
---|---|
& |
ET |
| |
OU |
^ |
OU Exclusif |
~expr |
Complément binaire à 1 (les 0 deviennent des 1; les 1 deviennent des 0) |
<< |
Décalage à gauche |
>> |
Décalage à droite |
Voici un exemple d’utilisation des opérateurs binaires et de décalage :
Il reste quelques opérateurs, vous en avez déjà vu la plupart dans les autres exemples.
Opérateur | Nom | Signification |
---|---|---|
() |
Applique une fonction | Représente un appel de fonction |
[] |
Accède à une liste | Réfère à la valeur dans la liste à l’index spécifié |
expr1 ? expr2 : expr3 |
Conditionnel | Si expr1 est vraie, exécute expr2; sinon, exécute expr3 |
. |
Accède un membre | Réfère à une propriété d’une expression; exemple: foo.bar sélectionne la propriété bar de l’expression foo |
.. |
Cascade | Vous permet d’effectuer plusieurs opérations sur les membres d’un même objet; décrit dans Classes |
Vous pouvez contrôler le flux d’exécution de votre code Dart en utilisant :
if
et else
boucles for
boucles while
et do
-while
break
et continue
switch
et case
assert
Vous pouvez aussi modifier le flux en utilisant try-catch
et throw
, comme expliqué dans Exceptions.
Dart permet les instructions if
avec des instructions else
optionnelles, comme le montre l’exemple suivant.
Voir aussi les expressions conditionnelles, qui sont abordées dans Autres opérateurs.
N’oubliez pas, contrairement à JavaScript, Dart considère toutes les valeurs autre que true
comme false
.
Voir Booléens pour plus d’informations.
Vous pouvez itérer avec une boucle for
classique. Par exemple :
Les closures à l’intérieur d’une boucle capturent la valeur de l’index, évitant un piège classique existant en Javascript. Par exemple, prenons :
La sortie est 0
puis 1
comme voulu. A l’inverse, l’exemple afficherait 2
puis 2
en JavaScript.
Si l’objet sur lequel on itére est un Iterable, vous pouvez utiliser la
méthode forEach()
.
Utiliser forEach()
est une bonne solution si vous n’avez pas besoin de connaître la valeur courante du compteur incrémental:
Les classes Iterable tel que List et Set autorisent aussi les itérations for-in
:
Une boucle while
évalue la condition avant la boucle :
Une boucle do
-while
évalue la condition après la boucle :
Utiliser break
pour stopper une boucle :
Utiliser continue
pour passer à la prochaine itération de la boucle:
Vous pourriez écrire cet exemple différement si vous utilisez un Iterable comme une liste ou un set :
Les instructions switch en Dart comparent les entiers, chaines de caractères,
ou constantes de compilations en utilisant ==
. Les objets comparés doivent
tous être des instances de la même classe (et non pas l’un de ses sous types),
et la classe doit surcharger ==
.
Les types énumérés fonctionnent également dans les instructions switch
.
Chaque case
non vide prend fin avec une instruction break
, c’est une règle.
Une autre façon possible de fermer un case
non vide et une instruction continue
,
throw
ou return
.
Utilisez une clause default
pour exécuter du code lorsque aucune clause ne correspond :
L’exemple suivant omet l’instruction break
de la clause case
, ce qui génère une erreur :
Cependant, Dart permet une clause case
vide, autorisant un bloc passant au travers :
Si vous voulez vraiment passer au travers, vous pouvez utiliser une instruction continue
et une étiquette :
Une clause case
peut avoir des variables locales, qui peuvent être visible seulement
à l’intérieur de la portée de cette clause.
Utiliser une instruction assert pour empécher l’exécution normale si une condition booléene est fausse. Vous pouvez trouver des exemples de déclarations assert tout au long de cet ouvrage. En voici un de plus :
A l’intérieur des parenthèses, après assert
, vous pouvez placer une expression
qui renvoit une valeur booléene ou une fonction. Si la valeur d’une expression ou
de la fonction retournée est vraie, l’assertion réussit et l’exécution continue.
Si elle est fausse, l’assertion échoue et une exception (une
AssertionError) est levée.
Votre code Dart peut lever et attraper des exceptions. Les exceptions sont des erreurs indiquant que quelque chose d’inattendu est survenu. Si l’exception n’est pas attrapée, l’isolate qui a levé l’exception est suspendu, et l’isolate et son programme sont interrompus.
Contrairement à Java, toutes les exceptions Dart sont des exceptions incontrôlées. Les méthodes ne déclarents par quelles exceptions elles sont susceptible de lever, et il ne vous est pas demandé de toutes les attraper.
Dart founit les types Exception et Error , ainsi que de nombreux sous types prédéfinis. Vous pouvez bien entendu définir vos propre exception. Cependant, les programmes Dart peuvent lever n’importe quel objet non nul comme une exception, pas seulement des objets Exception et Error.
Voici un exemple de comment lever une exception :
Vous pouvez également lever n’importe quel type d’objet :
Lever une exception étant une expression, vous pouvez lever des exceptions dans les instructions =>, ainsi que dans tout autre endroit permettant les expressions :
Attraper, ou capturer une exception interromp la propagation de l’exception. Attraper une exception vous offre la possibilité de la traiter :
Pour traiter du code qui peut lever plus d’un type d’exception, vous pouvez spécifier de multiples clauses catch. La première clause catch qui correspond au type de l’exception levée traite l’exception. Si la clause catch ne spécifie pas de type, cette clause peut traiter n’importe quel type d’objet levé :
Comme le montre le code précédent, vous pouvez utiliser au choix on
ou catch
ou les deux.
Utilisez on
quand vous avez besoin de spécifier le type de l’exception. Utilisez catch
lorque traitement de l’exception a besoin de l’objet exception.
Pour s’assurer qu’une partie de code s’exécute qu’une exception est été levée ou pas, utilisez la clause finally
. Si aucune clause catch
ne correspond à l’exception, cette dernière est propagée après que la clause finally
n’ait été lancée :
La clause finally
s’exécute après n’importe quelle clause catch
:
Apprenez en davantage en lisant la section Exceptions.
Dart est un langage orienté objet avec des classes et un héritage à base de mixin. Chaque objet est une instance d’une classe, et toutes les classes descendent d’Object. L’héritage à base de mixin signifie qu’un corps d’une classe peut être réutilisé dans un héritage multiple, même si toute classe (excepté pour Object) a une et une seule classe mère (superclass).
Pour créer un objet, vous pouvez utiliser le mot-clé new
avec le
constructeur d’une classe. Les noms des constructeurs peuvent être soit
NomClasse
soit
NomClasse.identifiant
. Par exemple :
Les objets ont des membres constitués de fonctions et de données (respectivement méthodes et variables d’instance). Lorsque vous appelez une méthode, vous l’invoquez sur un objet : la méthode a accès aux fonctions et données de l’objet.
Utilisez un point (.) pour faire référence à une variable d’instance ou une méthode :
Utilisez l’opérateur en cascade (..) lorsque vous voulez enchainer des opérations sur les membres d’un même objet :
Certaines classes fournissent des constructeurs de constantes. Pour
créer une constante à la compilation en utilisant constructeur de
constantes, utilisez const
au lieu de new
:
La construction de deux constantes de compilation identiques résulte en une seule et unique instance :
Les sections suivantes montrent comment implémenter des classes.
Voici comment déclarer des variables d’instance :
Toutes les variables d’instance non initialisée ont la valeur null
.
Toutes les variables d’instance génèrent une méthode getter implicite. Les variables d’instance non-finales génèrent également une méthode setter implicite. Pour plus de détails, voir Getters et setters.
Si vous initialisez une variable d’instance là où elle est déclarée (plutôt que dans un constructeur ou dans une méthode), la valeur est définie quand l’instance est créée, c’est-à-dire, avant l’exécution du constructeur et de sa liste d’initialisation.
Déclarez un constructeur en créant une fonction de même nom que sa classe (plus, éventuellement, un identifiant comme décrit dans Constructeurs nommés). La forme la plus commune de constructeur, le constructeur générateur, créé une nouvelle instance d’une classe :
Le mot clé this
réfère à l’instance courante.
Le modèle consistant à assigner un argument du constructeur à une variable d’instance est tellement commun, Dart possède un sucre syntaxique pour le rendre facile :
Si vous ne déclarez pas de constructeur, un constructeur par défaut est fourni pour vous. Le constructeur par défaut n’a pas d’argument et invoque le constructeur sans arguement de la classe parente.
Les sous classes n’héritent pas des constructeurs de leur classes parentes. Une sous classe qui ne déclare pas de constructeur possède seulement le constructeur par défaut (pas d’argument, pas de nom).
Utilisez un constructeur nommé pour implémenter plusieurs constructeurs pour une classe ou pour plus de clareté :
Souvenez vous que les constructeurs ne sont pas hérités, ce qui sigifie qu’un constructeur nommé d’une classe parente n’est pas hérité par sa sous classe. Si vous désirez qu’une sous classe soit créé avec un constructeur nommé défini par une super classe, vous devez implémenter ce constructeur dans la sous classe.
Par défaut, un constructeur d’une sous classe appelle le constructeur non nommé et à
zéro paramètre de la superclasse.
Si la superclasse n’a pas ce type de constructeur, alors, vous devez appeler manuellement un des constructeur
de la superclasse. Indiquez le constructeur de la superclasse après le symbole deux points (:
), juste
avant le corps du constructeur (s’il y en a un).
Du fait que les paramètres du constructeur de la superclasse sont évalués avant d’invoquer le constructeur, un paramètre peut être une expression telle qu’un appel de fonction :
Hormis l’invocation du constructeur de la classe parente, vous pouvez également initialiser les variables d’instance avant que le corps du constructeur ne soit exécuté. Séparez les paramètres d’initialisation par des virgules.
Parfois, un constructeur a pour seul objectif de rediriger vers un autre constructeur de la même classe. Le corps d’un constructeur de redirection est vide avec l’appel de constructeur cible apparaissant après le symbole (:).
Si votre classe produit des objets qui ne changent jamais, vous faites de
ces objets des constantes de compilation. Pour ce faire, définissez un constructeur
const
et assurez vous que toutes les variables d’instance sont final
.
Utilisez le mot clé factory
lorsque vous implémentez un constructeur
qui ne crée pas toujours une nouvelle instance de sa classe.
Par exemple, un constructeur de type fabrique peut retourner une instance à partir
d’un cache, ou il pourrait encore retourner une instance d’un sous-type.
L’exemple suivant présente un constructeur de type fabrique qui retourne un objet depuis un cache :
Pour invoquer un constructeur de type fabrique, utilisez le mot clé new
:
Les méthodes sont des fonctions qui fournissent un comportement pour un objet.
Les méthodes d’instance sur des objets peuvent accéder aux variables d’instance et this
.
La méthode distanceA()
dans le code suivant suivant est un exemple de méthode d’instance.
Les getters et setters sont des méthodes spéciales qui fournissent des accès
en lecture et écriture aux propriétés d’un objet. Rappelons que chaque
variable d’instance a un getter implicite, et un setter si elle est affectable.
Vous pouvez créer des propriétés additionelles en implémentant des getters
et setters, en utilisant les mots clés get
et set
:
Avec les getters et setters, vous pouvez commencer avec des variables d’instances, et plus tard les encapsuler avec des méthodes, tout cela sans changer le code client.
Les méthodes d’instance, de getters et setters peuvent être abstrait, définissant une interface mais laissant l’implémentation à d’autres classes. Pour rendre une méthode abstraite, utiliser le point virgule (;) au lieu d’un corps de méthode :
Appeler une méthode abstraitre produit une erreur d’exécution.
Voir aussi Classes abstraites.
Vous pouvez surcharger les opérateurs présents dans le tableau suivant.
Par exemple, si vous définissez une classe Vecteur, vous pourriez définir
une méthode +
pour additionner deux vecteurs.
< |
+ |
| |
[] |
> |
/ |
^ |
[]= |
<= |
~/ |
& |
~ |
>= |
* |
<< |
== |
– |
% |
>> |
Voici un exemple d’une classe qui surcharge les opérateurs +
et -
:
Si vous surchargez ==
, vous devriez aussi surcharger le getter hashCode
de Object.
Pour un exemple de surcharge de ==
et hashCode
, voir
Implémentation des clés d’un dictionnaire.
Pour plus d’information sur la surcharge, de façon générale, voir Etendre une classe.
Utilisez le modificateur abstract
pour définir une classe abstraite,
c’est-à-dire, une classe qui ne peut pas être instanciée. Les classes
abstraites sont utiles pour définir des interfaces, souvent avec quelques
implémentations. Si vous voulez que votre classe abstraite apparaisse
comme instanciable, définissez un Constructeur de type fabrique.
Les classes abstraites ont souvent des méthodes abstraites. Voici un exemple de déclaration d’une classe abstraite qui a une méthode abstraite :
La classe suivante n’est pas abstraite, et elle peut être instanciée, même si elle définit une méthode abstraite :
Toute classe définie implicitement une interface contenant tous les membres de la classe ainsi que toutes interfaces qu’elle implémente. Si vous créez une classe A qui supporte l’API de la classe B sans hériter de l’implémentation de B, la classe A doit implémenter l’interface B.
Une classe implémente une ou plusieurs interfaces en les déclarant dans la clause
implements
et en fournissant l’API requise par ces interfaces.
Par exemple :
Voici un exemple de classe implémentant plusieurs interfaces :
Utilisez extends
pour créer une sous-classe, et super
pour référer
à la super-classe :
Les sous-classes peuvent étendre les méthodes d’instance, les getters et setters.
Voici un exemple de surcharge de la méthode noSuchMethod()
de la classe Object.
Celle-ci est appelée par n’importe quel code qui tente d’utiliser une méthode ou une variable
d’instance inexistante :
Vous pouvez utiliser l’annotation @override
pour indiquer que vous surchargez intentionnellement un membre :
Si vous utilisez noSuchMethod()
pour implémenter tous les getters, setters, méthodes
pour une classe alors vous pouvez utiliser l’annotation @proxy
pour éviter les avertissements :
Pour plus d’information sur les annotations, jetez un oeil à Metadata.
Enumerated types, often called enumerations or enums, are a special kind of class used to represent a fixed number of constant values.
Declare an enumerated type using the enum
keyword:
Each value in an enum has an index
getter,
which returns the zero-based position of the value in the enum declaration.
For example, the first value has index 0,
and the second value has index 1.
To get a list of all of the values in the enum,
use the enum’s values
constant.
You can use enums in switch statements.
If the e in switch (e)
is explicitly typed as an enum,
then you’re warned if you don’t handle all of the enum’s values:
Enumerated types have the following limits:
For more information, see the Dart Language Specification.
Les mixins sont une façon de réutiliser le code d’une classe dans des héritages multiples.
Pour utiliser un mixin, utilisez le mot-clé with
suivi par un ou
plusieurs noms de mixin. L’exemple suivant montre deux classes qui
utilisent les mixins :
Pour implémenter un mixin, créez une classe qui étend Object, ne
déclare pas de constructeurs, et n’a aucun appels à super
. Par
exemple:
Pour plus d’information, voir l’article Mixins dans Dart.
Utilisez le mot clé static
pour implémenter des variables et méthodes de classes.
Les variables statiques (variables de classes) sont utiles un état et des constantes communs à la classe :
Les variables statiques sont initialisées lorsqu’elles sont utilisées.
Les méthodes statiques (méthodes de classes) n’opèrent pas sur une instance, et ainsi
n’ont pas accès à this
. Par exemple :
Vous pouvez utiliser des méthodes statiques comme constantes de compilation. Par exemple, vous pouvez passer une méthode statique comme un paramètre à un constructeur constant.
Si vous regardez sur la documentation de l’API pour le type tableau de
base, List, vous verrez
que le type est en fait List<E>
. La notation <…> marque la liste
comme un type générique (ou paramétré), c’est-à-dire, un type qui
prend des types en paramètre. Par convention, les variables de type ont
des noms d’une seule lettre, tels que E, T, S, K, et V.
Etant donné que les types sont optionnels en Dart, vous n’êtes jamais obligé d’utiliser les génériques. Il se peut que vous vouliez l’utiliser, pour la même raison que vous voudriez utiliser d’autres types dans votre code : les types (génériques ou non) vous permettent de documenter et d’annoter votre code, rendant votre intention plus claire.
Par exemple, si votre intention est qu’une liste ne contienne que des
chaînes de caractères, vous pouvez la déclarer comme List<String>
(le
lire comme “liste de string”). De cette façon, vous, vos co-programmeurs,
et vos outils (comme votre IDE et la VM Dart en mode vérification)
peuvent détecter qu’assigner autre chose qu’une chaîne de caractères à
la liste est probablement une erreur. Voici un exemple :
Une autre raison pour utiliser les génériques est de réduire la duplication de code. Les génériques vous permettent de partager une seule interface et implémentation entre de nombreux types, tout en tirant avantage du mode vérification et des alertes préventives de l’analyse statique. Par exemple, disons que vous créez une interface pour mettre en cache un objet :
Vous découvrez que vous voulez une version spécifique de cette interface pour les chaînes de caractères, donc vous créez une autre interface :
Plus tard, vous décidez que vous voulez une version spécifique de cette interface pour les nombres… et ainsi de suite.
Les types génériques peuvent vous sauver de la difficulté de créer toutes ces interfaces. A la place, vous pouvez créer une interface unique qui prend un type en paramètre :
Dance ce code, T est un type de remplacement. C’est un paramètre fictif que vous pensez comme un type qu’un développeur définira plus tard.
Les listes et les dictionnaires peut être paramétrés. Les valeurs
littérales paramétrées sont comme les valeurs littérales que vous avez
déjà vues, excepté que vous ajoutez
<type>
pour les listes avant le crochet
ouvrant ou
<typeClé, typeValeur>
pour les
dictionnaires avant l’accolade ouvrante. Vous pouvez utiliser les
valeurs littérales paramétrées lorsque vous voulez des alertes de type
en mode vérification. Voici un exemple d’utilisation de valeurs
littérales typées :
Pour spécifier un ou plusieurs types en utilisant un constructeur,
mettre les types entre chevrons (<...>
) juste après le nom de la
classe. Par exemple :
Le code suivant crée un dictionnaire qui a des entiers pour clé et des types Vue pour valeur :
Les types génériques Dart sont réifiés, c’est-à-dire qu’ils portent l’information de leur type à l’exécution. Par exemple, vous pouvez tester le type d’une collection, même en mode production :
Cependant, l’expression is
vérifie le type de la collection
seulement, pas ceux des objets à l’intérieur. En mode production, une
List<String>
peut avoir des éléments autres que des chaînes de
caractères. La solution est de, soit vérifier le type de chaque élément
ou soit d’entourer le code manipulant les éléments dans un gestionnaire
d’exception (voir Exceptions).
Pour plus d’information sur les génériques, voir Optional Types in Dart.
Les instructions import
, part
, et library
peuvent vous aider à créer
une base de code modulaire et partageable. Les bibliothèques fournissent non seulement
des APIs, mais sont une unité de cloisonement : les identifiants qui commencent
par un underscore (_) ne sont visibles qu’au sein de la bibliothèque.
Toute application Dart est une bibliothèque, même si elle n’utilise pas une directive library
.
Les bibliothèques peuvent être distribuées comme paquet. Voir Pub Package and Asset Manager pour davantage d’informations sur pub, un gestionnaire de paquets inclu dans le SDK.
Utilisez import
pour spécifier comment un espace de nommage d’une bibliothèque est utilisé
dans le cadre d’une autre bibliothèque.
Par exemple, les applications Web Dart utilisent généralement la bibliothèque dart:html, qu’elles peuvent importer ainsi :
Le seul argument nécessaire à import
est une URI spécifiant la bibliothèque.
Pour les bibliothèques natives, les URI ont le préfixe spécial dart:
.
Pour les autres bibliothèques, vous pouvez utilisez un chemin de votre système
de fichier ou le préfixe package:
. Le préfixe package:
désigne les bibliothèques
fournies par un gestionnaire de paquets tel que l’outil pub. Par exemple :
Si vous importez deux bibliothèques qui ont des identifiants en conflit, vous avez la possibilité de spécifier un préfixe pour l’une ou les deux de ces bibliothèques. Par exemple, si bibliothèque1 et bibliothèque2 possèdent toutes deux une classe Element, vous pouvez avoir le code suivant :
Si vous ne désirez utiliser qu’une partie d’une bibliothèque, vous avez la possibilité de choisir quoi importer de celle-ci. Par exemple :
Deferred loading (also called lazy loading) allows an application to load a library on demand, if and when it’s needed. Here are some cases when you might use deferred loading:
To lazily load a library, you must first
import it using deferred as
.
When you need the library, invoke
loadLibrary()
using the library’s identifier.
In the preceding code,
the await
keyword pauses execution until the library is loaded.
For more information about async
and await
,
see Support de l’asynchronisme.
You can invoke loadLibrary()
multiple times on a library without problems.
The library is loaded only once.
Keep in mind the following when you use deferred loading:
loadLibrary()
into the namespace that you define
using deferred as namespace
.
The loadLibrary()
function returns a Future.Utilisez library
pour nommer une bibliothèque, et part
pour spécifier des fichiers additionnels
dans la bibliothèque.
Utilisez l'identifiant library
pour spécifier le nom de la bibliothèque courante :
Pour ajouter un fichier d’implémentation, ajoutez
part uriDuFichier
dans le fichier qui a la déclaration library
, où uriDuFichier
est le chemin vers le fichier d’implémentation. Indiquez alors
part of identifiant
,
où identifiant est le nom de la bibliothèque. L’exemple suivant
utilise part
et part of
pour implémenter une bibliothèque de trois fichiers.
Le premier fichier jeudeballe.dart
, déclare la bibliothèque jeudeballe, importe
les bibliothèques qui lui sont nécessaires, et indique que balle.dart
et util.dart
font partie de la bibliothèque :
Le deuxième fichier, balle.dart
, implémente une partie de la bibliothèque jeudeballe :
Le troisième fichier util.dart
, implémente le reste de la bibliothèque jeudeballe :
Vous pouvez combiner et ré-empaqueter des bibliothèques en ré-exportant tout ou partie de celles-ci. Par exemple, vous pouvez avec une énorme bibliothèque que vous implémentez sous forme de plus petites bibliothèques. Ou alors vous pouvez créer un bibliothèque qui fournit un sous ensemble de méthodes d’une autre bibliothèque.
Dart has added new language features
to support asynchronous programming.
The most commonly used of these features are
async
functions and await
expressions.
Dart libraries are full of functions that return Future or Stream objects. These functions are asynchronous: they return after setting up a possibly time-consuming operation (such as I/O), without waiting for that operation to complete.
When you need to use a value represented by a Future, you have two options:
async
and await
Similarly, when you need to get values from a Stream, you have two options:
async
and an asynchronous for loop (await for
)Code that uses async
and await
is asynchronous,
but it looks a lot like synchronous code.
For example, here’s some code that uses await
to wait for the result of an asynchronous function:
To use await
, code must be in a function marked as async
:
You can use try
, catch
, and finally
to handle errors and cleanup in code that uses await
:
An async function is a function whose body is marked with
the async
modifier.
Although an async function might perform time-consuming operations,
it returns immediately—before
any of its body executes.
Adding the async
keyword to a function makes it return a Future.
For example, consider this synchronous function,
which returns a String:
If you change it to be an async function—for example, because a future implementation will be time consuming—the returned value is a Future:
Note that the function’s body doesn’t need to use the Future API. Dart creates the Future object if necessary.
An await expression has the following form:
await expression
You can use await
multiple times in an async function.
For example, the following code waits three times
for the results of functions:
In await expression
,
the value of expression
is usually a Future;
if it isn’t, then the value is automatically wrapped in a Future.
This Future object indicates a promise to return an object.
The value of await expression
is that returned object.
The await expression makes execution pause until that object is available.
If await
doesn’t work, make sure it’s in an async function.
For example, to use await
in your app’s main()
function,
the body of main()
must be marked as async
:
An asynchronous for loop has the following form:
await for (variable declaration in expression) { // Executes each time the stream emits a value. }
The value of expression
must have type Stream.
Execution proceeds as follows:
To stop listening to the stream,
you can use a break
or return
statement,
which breaks out of the for loop
and unsubscribes from the stream.
If an asynchronous for loop doesn’t work,
make sure it’s in an async function.
For example, to use an asynchronous for loop in your app’s main()
function,
the body of main()
must be marked as async
:
For more information about asynchronous programming, see the dart:async section of the library tour. Also see the articles Dart Language Asynchrony Support: Phase 1 and Dart Language Asynchrony Support: Phase 2, and the Dart language specification.
Les navigateurs modernes, même sur les plateformes mobiles, fonctionnent sur des processeurs multi-coeurs. Pour tirer avantage de ces coeurs, les développeurs utilisent traditionnellement des treads à mémoire partagée s’éxcutant en parrallèle. Cependant, le parallélisme à état partagé est source d’erreur et peut ammener à du code complexe.
Au lieu d’utiliser les threads, tout code Dart tourne au sein d’un isolates. Chaque isolate possède sa propre mémoire tas, permettant de garantir qu’aucun état d’isolate n’est accessible depuis un autre isolate.
En Dart, les fonctions sont des objets, tout comme les chaines de caractères et les nombres sont des objets. Un typedef, ou alias de type de fonction, donne un nom à un type de fonction que vous pouvez utiliser pour déclarer un champ ou un type de retour. Un typedef garde les informations de type lorsqu’un type de fonction est assigné à une variable.
Voici un exemple de code qui n’utilise pas de typedef:
L’information de type est perdue lors de l’assignation de f
à compare
.
Le type de f
est (Object,
Object)
→ int
(où → signigie retourne), le type de compare
est également Function. Si nous adaptons le code pour utiliser des noms explicites et garder l’information,
les développeurs ainsi que les outils peuvent utiliser cette information.
Du fait que les typedefs sont des alias, ils offrent un moyen de vérifier le type de n’importe quelle fonction. Par exemple :
Utilisez les metadata pour donner de l’information complémentaire à votre code.
Une annotation metadata commence par le caratère @
, suivi par soit une référence
à une constante de compilation (telle que deprecated
) soit un appel à un constructeur constant.
Trois annotations sont disponibles pour tout code Dart : @deprecated
,
@override
, et @proxy
. Pour des exemples d’utilisation de @override
et
@proxy
, voir Etendre une classe.
Voici un exemple d’utilisation de l’annotation @deprecated
:
Vous pouvez définir vos propres annotations metadata. Voici un exemple de définition de l’annotation @todo qui prend deux arguments :
Et voici un exemple d’utilisation de cette annotation @todo :
Les metadatas peuvent apparaitre avant une déclaration de librairie, classe, typedef, paramètre de type, constructeur, factory, fonction, champ, paramètre, ou variable et avant un import ou export de directive. Vous pouvez récupérer les metadatas durant l’execution en utilisant la réflexion.
Dart supporte les commentaires sur une ou plusieurs lignes et les commentaires de documentation.
Un commentaire d’une ligne commence par //
. Tout entre //
et la
fin de la ligne est ignoré par le compilateur Dart.
Un commentaire de plusieurs lignes commence par /*
et se termine par */
.
Tout entre /*
et */
est ignoré par le compilateur Dart (à moins que le commentaire ne soit un commentaire de documentation; voir la section suivante). Les commentaires de plusieurs lignes peuvent s’imbriquer.
Les commentaires de documentation sont des commentaires d’une ou plusieurs lignes qui commencent
avec ///
ou /**
. Utiliser ///
sur plusieurs lignes consécutives a le même effet qu’un commentaire de documentation sur plusieurs lignes.
Dans un commentaire de documentation, le compilateur Dart ignore tout text sauf si celui-ci est entouré par des crochets. En utilisant des crochets, vous pouvez référer à une classe, une méthode, un champ, une variable globale, une fonction et des paramètres. Les noms entre crochets sont résolues dans le champ lexical de l’élément documenté.
Voici un exemple de commentaires de documentation qui référencient d’autres classes et arguments:
Dans la documentation générée, [Nourriture]
devient un lien pour la documentation d’API pour la classe Nourriture.
Pour parser du code Dart et générer de la documentation HTML, vous pouvez utiliser l’outil de génération de documentation. du SDK. Pour un exemple de documentation générée, voir la Documentation de l’API Dart.. Pour des conseils sur la structuration des commentaires, voir le Guide pour les commentaires de documentation Dart.
Ce chapitre résume les fonctionnalités les plus communes dans le langage Dart. D’autres fonctionnalités sont en train d’être implémentées, et nous espérons qu’elles ne casseront pas le code existant. Pour plus d’information, voir la Spécification du Langage Dart et les articles tel que Idiomatic Dart.