doc: french doc "développez en Nit"
[nit.git] / doc / developpez / poo / poo.tex
1 \part{Programmation orientée objet}
2 \chapter{Concept de classe}
3 L'idée de base de la programmation orientée objet est de rassembler dans une même entité les données et les traitements qui s'y appliquent. Dans cette partie, nous allons voir comment Nit permet de développer en objet.
4
5 Une classe est le support de l'encapsulation : c'est un ensemble de données et de fonction regroupées dans une même entité. Une classe est une description abstraite d'un objet. Les fonctions qui opèrent sur les données sont appelées des méthodes. Les données en elles-mêmes sont appelées des attributs. Les attributs et les méthodes représentent les propriétés de la classe. Instancier une classe consiste à créer un objet sur son modèle.
6
7 Rappelons qu'en Nit absolument TOUT est objet.
8
9 Pour accéder à une classe il faut en déclarer une instance ou objet.
10
11 Une classe se compose en deux parties : un en-tête et un corps. Le corps peut être divisé en deux sections : la déclaration de ses propriétés (attributs et la définition des méthodes. Les méthodes et les données sont pourvues d'attributs de visibilité qui gère leur accessibilité par les composants hors de la classe.
12
13 Une classe comporte donc sa déclaration, des attributs et la définition de ses méthodes.
14
15 En Nit, toutes les classes héritent forcément de la classe |Object|. Cette relation d'héritage est implicite, c'est à dire qu'il n'y a pas besoin de la déclarer.
16
17 \section{Syntaxe et déclaration d'une classe}
18 La syntaxe de déclaration d'un classe est la suivante :
19 \begin{lstlisting}[language=Nit]
20 [redef][abstract] class nomDeClasse [super classeMere...]
21 ...
22 end
23 \end{lstlisting}
24
25 Les modificateurs de classe sont :
26 \begin{description}
27 \item[abstract] Une classe abstraite est une classe contenant une ou plusieurs méthodes abstraites, qui n'ont pas de déclaration explicite. Une classe déclarée |abstract| ne peut pas être instanciée : il faut définir une classe qui hérite de cette classe et qui implémente les méthodes nécessaires pour ne plus être abstraite (voir section~\ref{classes-abstraites} \nameref{classes-abstraites}).
28 \item[redef] Ce modificateur permet de raffiner une classe pour y ajouter ou modifier des propriétés (voir section~\ref{redef} \nameref{redef}).
29 \end{description}
30
31 Le mot clé |super| permet de spécifier une ou des superclasses éventuelles : ce mot clé permet de préciser une classe mère dans une relation d'héritage. Nit supporte l'héritage multiple, il est donc possible d'utiliser plusieurs fois |super| sur la même classe (voir chapitre~\ref{heritage} \nameref{heritage}).
32
33 L'ordre des méthodes dans une classe n'a pas d'importance. Si dans une classe, on rencontre d'abord la méthode A puis la méthode B, B peut être appelée sans problème dans A.
34
35 \chapter{Objets}
36 La classe est la description d'un objet. Un objet est une instance d'une classe. Pour chaque instance d'une classe, le code est le même, seules les données (valeurs des attributs) sont différentes à chaque objet.
37
38 \section{Instancier une classe : créer un objet}
39 L'opérateur |new| se charge de créer une nouvelle instance d'une classe et de l'associer à une variable.
40
41 Exemple :
42 \lstinputlisting[language=Nit]{./poo/listings/new1_s.nit}
43
44 Cela n'est pas obligatoire grâce au typage adaptatif (voir chapitre~\ref{typage-adaptatif} \nameref{typage-adaptatif}) mais on peut aussi préciser le type sur la variable qui va recevoir l'instance :
45 \lstinputlisting[language=Nit]{./poo/listings/new2_s.nit}
46
47 En Nit, toutes les classes sont instanciées par allocation dynamique à part les types universels (voir chapitre~\ref{universal} \nameref{universal}). Dans l'exemple précédent, la variable |instance| contient une référence sur la classe instanciée (contient l'adresse de l'objet qu'elle désigne : attention toutefois, il n'est pas possible de manipuler ou d'effectuer des opérations directement sur cette adresse comme en C).
48
49 \lstinputlisting[language=Nit]{./poo/listings/new3_s.nit}
50
51 L'opérateur |new|, lorsqu'il instancie la classe, appelle une méthode particulière de cet objet : le constructeur (voir chapitre~\ref{constructeurs} \nameref{constructeurs}).
52
53 \section{Durée de vie d'un objet}
54 La durée de vie d'un objet ne correspond pas forcément à la durée de vie du programme.
55
56 La durée de vie d'un objet passe par trois étapes :
57 \begin{description}
58 \item[La création] de l'objet grâce à l'opérateur d'instanciation |new|.
59 \item[L'utilisation] de l'objet en appelant ses méthodes.
60 \item[La destruction] de l'objet, c'est à dire la libération de la mémoire qu'occupe l'objet. En Nit, la libération de la mémoire est automatiquement effectuée par le ramasse miette (garbage collector). Quand le ramasse miette découvre un objet qui ne sera plus utilisé dans le programme alors il va automatiquement le supprimer. Il n'existe pas d'instruction \lstinline[language=C++]{delete} comme en C++.
61 \end{description}
62
63 \section{Références et comparaison d'objets}
64 Les variables de type objet que l'on déclare ne contiennent pas un objet mais une référence vers cet objet. Lorsque l'on écrit |instance1 = instance2|, on copie la référence |instance1| dans |instance2| : |instance1| et |instance2| pointent sur le même objet. Par défaut, l'opérateur |==| permet de comparer ces références. Deux objets avec des propriétés identiques sont deux objets distincts.
65
66 \lstinputlisting[language=Nit]{./poo/listings/comparaison1_s.nit}
67 Si on souhaite comparer deux objets en se basant sur la valeur de leurs attributs, il est possible de redéfinir l'opérateur |==| dans leur classe (voir section~\ref{redef-operateur} \nameref{redef-operateur}).
68
69 Par exemple, l'opérateur |==| a été redéfinit pour la classe String il est donc possible de comparer deux String en se basant sur leurs valeurs :
70 \lstinputlisting[language=Nit, linerange=1-5]{./poo/listings/comparaison2_c.nit}
71
72 Il est possible de comparer le type de deux objets, c'est à dire de vérifier si deux objets sont de la même classe ou non. Pour cela nous allons utiliser la méthode |is_same_type(o:Object)|. Cette méthode est introduite dans la classe |Object|, comme toutes les classes en héritent implicitement, elle est disponible dans toutes les classes.
73
74 \lstinputlisting[language=Nit, linerange=8-9, firstnumber=last]{./poo/listings/comparaison2_c.nit}
75
76 Pour vérifier que deux variables sont en fait des références vers le même objet, il est possible d'utiliser le mot-clé |is| :
77 \lstinputlisting[language=Nit, linerange=6, firstnumber=last]{./poo/listings/comparaison2_c.nit}
78
79 \section{Objet null}
80 En Nit, il existe une particularité pour l'objet |null|. Contrairement à Java il ne peut pas être utilisé à la place de n'importe où. En effet, Nit propose un mécanisme permettant de limiter statiquement les erreurs fréquentes de type |nullPointerException|.
81
82 Pour qu'une variable puisse prendre la valeur |null|, elle doit avoir été déclarée explicitement avec le type |nullable| :
83 \lstinputlisting[language=Nit]{./poo/listings/nullable1_c.nit}
84 Dans l'exemple précédent, si on omet de préciser le type comme |nullable|, le compilateur Nit lèvera une erreur au moment de la compilation.
85
86 \chapter{Modificateurs d'accès}
87 Les modificateurs d'accès assurent le contrôle des conditions d'héritage, d'accès aux éléments et de modification de données par les autres classes. Ils s'appliquent aussi bien aux classes, qu'aux méthodes et attributs.
88
89 \section{Visibilité des entités}
90 De nombreux langages orientés objet introduisent des attributs de visibilité pour réglementer l'accès aux classes et aux objets, aux méthodes et aux données.
91
92 Il existe 2 modificateurs qui peuvent être utilisés pour définir les attributs de visibilité des entités (classes, méthodes ou attributs) : |private| et |protected|. Leur utilisation permet de définir des niveaux de protection différents (présentés dans un ordre croissant de niveau de protection offert) :
93
94 %TODO reformuler
95 \begin{description}
96 \item[Par défaut : public] Il n'existe pas de mot clé pour définir ce niveau, qui est le niveau par défaut lorsqu'aucun modificateur n'est précisé. Un attribut ou une méthode déclaré public est visible par tous les autres objets. Dans la philosophie orientée objet aucun attribut ne devrait être déclaré public : il est préférable d'écrire des méthodes pour le consulter et le modifier.
97 \item[protected] Si un attribut ou une méthode est déclarée |protected|, seules les méthodes de ses sous classes pourront y accéder.
98 \item[private] C'est le niveau de protection le plus fort. Les composants ne sont visibles qu'à l'intérieur du fichier où est définie la classe.
99 \end{description}
100
101 Ces modificateurs d'accès sont mutuellement exclusifs.
102
103 \section{Classes abstraites}
104 \label{classes-abstraites}
105
106 Le mot clé |abstract| peut être appliqué à une classe qui ne doit pas être instanciée. Cela permet de créer une classe qui sera une sorte de moule : toutes les classes dérivées pourront profiter des méthodes héritées.
107
108 \lstinputlisting[language=Nit]{./poo/listings/abstract1_c.nit}
109
110 Essayer d'instancier une classe abstraite générera une erreur de compilation.
111
112 \chapter{Méthodes}
113 Les méthodes sont des fonctions qui implémentent les traitements de la classe.
114
115 \section{Syntaxe et déclaration}
116 La syntaxe de la déclaration d'une méthode est :
117 \begin{lstlisting}[language=Nit]
118 [redef] [protected|private] fun nom_de_méthode[( arg1:Type, ... )][:Type_retourné] do
119 ...
120 end
121 \end{lstlisting}
122
123 \section{Types retournés}
124 Le type retourné correspond au type de l'objet qui est renvoyé par la méthode. Si la méthode ne retourne rien, alors on ne précise pas le type retourné.
125
126 La valeur de retour de la méthode doit être transmise par l'instruction |return|. Elle indique la valeur que prend la méthode et termine celle ci : toutes les instructions qui suivent |return| ne seront pas exécutées. Si des instructions sont placées après le mot clé |return|, une erreur |unreachable statement| sera levée lors de la compilation.
127
128 \lstinputlisting[language=Nit]{./poo/listings/methode1_c.nit}
129
130 Il est possible d'inclure une instruction |return| dans une méthode sensée ne rien retourner : cela permet de quitter la méthode.
131
132 \section{Passage de paramètres}
133 Les paramètres des méthodes sont déclarés entre parenthèses et séparés par des virgules. Le type et le nombre de paramètres déclarés doivent correspondre au type et au nombre de paramètres transmis. Si un méthode n'accepte pas de paramètres, il suffit de ne pas indiquer de parenthèses :
134 \lstinputlisting[language=Nit]{./poo/listings/methode2_c.nit}
135
136 Si plusieurs paramètres ont le même type, il est possible de le factoriser :
137 \lstinputlisting[language=Nit]{./poo/listings/methode7_c.nit}
138
139 Il n'est pas possible d'indiquer des valeurs par défaut dans les paramètres.
140
141 Lorsqu'un objet est passé comme paramètre à une méthode, cette dernière reçoit une référence qui désigne son emplacement mémoire d'origine et qui est une copie de la variable. Il est possible de modifier l'objet grâce à ces méthodes mais il n'est pas possible de remplacer la référence contenue dans la variable passée en paramètre : ce changement n'aura lieu que localement à la méthode.
142
143 \section{Envoi de message}
144 Un message est émis lorsqu'on demande à un objet d'exécuter l'une de ses méthodes.
145 La syntaxe d'appel d'une méthode est : |objet.methode(paramètre, ...)|
146
147 Si la méthode appelée ne contient aucun paramètre, il n'est pas nécessaire de mettre les parenthèses.
148
149 \lstinputlisting[language=Nit]{./poo/listings/methode4_s.nit}
150
151 Quand l'appel de méthode se fait en dehors d'une expression et qu'elle n'accepte qu'un seul paramètre, il est possible de ne pas utiliser les parenthèses :
152 \lstinputlisting[language=Nit]{./poo/listings/methode8_c.nit}
153
154 \section{Nombre variable de paramètres}
155 Il est possible d'indiquer qu'une méthode peut recevoir un nombre variable de paramètre grâce à l'opérateur |...|. Le paramètre sera alors considéré comme un tableau du type spécifié. L'envoi de message pourra alors contenir aucun, un ou plusieurs arguments pour le paramètre ainsi déclaré.
156
157 \lstinputlisting[language=Nit]{./poo/listings/methode9_c.nit}
158
159 Seulement un seul paramètre variable est autorisé dans une signature.
160
161 \section{Enchaînement de références}
162 Il est possible d'enchaîner les envois de messages aux retours des méthodes :
163 \lstinputlisting[language=Nit]{./poo/listings/methode5_c.nit}
164
165 Ici on appelle la méthode to\_upper de l'objet retourné par la méthode substring de l'objet "abcd".
166
167 \section{Mot-clé self}
168
169 Le mot-clé |self| est l'équivalent du |this| en Java. Il permet de pointer sur le receveur courant :
170 \lstinputlisting[language=Nit]{./poo/listings/self_c.nit}
171
172 \section{Redéfinition d'opérateurs}
173 \label{redef-operateur}
174
175 Avec Nit il est possible de définir ou redéfinir des opérateurs arithmétiques (|+|, |-|, |*|...) pour chacune de nos classes en suivant la même déclaration que pour une méthode normale :
176 \lstinputlisting[language=Nit]{./poo/listings/methode6_c.nit}
177
178 La seule restriction est que la méthode doit forcément prendre un et un seul paramètre et retourner quelque chose.
179
180 \section{Surcharge statique de méthode}
181 Pour des raisons liées à l'héritage, Nit ne permet pas la surcharge statique des méthodes. Il n'est donc pas possible de définir plusieurs fois une même méthode avec des types de paramètres différents.
182
183 \section{Méthodes abstraites}
184 Une méthode abstraite est une méthode déclarée avec le modificateur |is abstract| et sans corps. Elle correspond à une méthode dont on veut forcer l'implémentation dans une sous classe.
185
186 La syntaxe de la déclaration d'une méthode abstraite est :
187 \begin{lstlisting}[language=Nit]
188 [redef] [protected|private] fun nom_de_méthode[( arg1:Type, ... )][:Type_retourné] is abstract
189 \end{lstlisting}
190
191 \lstinputlisting[language=Nit, linerange=1-3]{./poo/listings/abstract2_c.nit}
192
193 Pour pouvoir utiliser la méthode, il faut la redéfinir dans une sous classe grâce au mot clé |redef| (voir section~\ref{redef} \nameref{redef}) :
194 \lstinputlisting[language=Nit, linerange=5-11, firstnumber=last]{./poo/listings/abstract2_c.nit}
195
196 Contrairement à d'autres langages, une classe n'est pas automatiquement abstraite dès lors qu'une de ses méthodes est déclarée abstraite. Cela est lié au concept de raffinement de classe que nous aborderons un peu plus tard (voir section~\ref{raffinement-classe} \nameref{raffinement-classe}).
197
198 Un appel à une méthode abstraite n'est pas décelé au moment de la compilation (toujours à cause du raffinement de classe) mais au moment de l'exécution.
199
200 \chapter{Attributs}
201 Les données d'une classe sont contenues dans des variables nommées attributs.
202
203 \section{Déclaration des attributs}
204 Le mot-clé |var| permet aussi de déclarer un attribut dans le corps de la classe. En Nit, tous les attributs doivent avoir un type explicite.
205
206 \lstinputlisting[language=Nit]{./poo/listings/attribut1_c.nit}
207
208 Chaque instance de la classe a accès à sa propre occurrence de l'attribut.
209
210 Il est possible de définir des valeurs par défaut pour chaque attribut :
211 \lstinputlisting[language=Nit]{./poo/listings/attribut2_c.nit}
212
213 \section{Accesseurs}
214 L'encapsulation permet de sécuriser l'accès aux données d'une classe, la bonne pratique consiste donc à créer des méthodes permettant d'accéder indirectement à ces données : les accesseurs. Nit applique à la lettre cette bonne pratique en empêchant systématiquement tout accès direct à l'attribut. En effet, pour chaque attribut déclaré dans une classe, Nit va généré un accesseur automatiquement pour en permettre l'accès.
215
216 Un accesseur est une méthode qui donne l'accès à une variable d'instance. Pour une variable d'instance, il peut ne pas y avoir d'accesseur, un accesseur en lecture (getter) et/ou un accesseur en écriture (setter).
217
218 Il reste tout de même possible de définir ses propres accesseurs :
219 \lstinputlisting[language=Nit]{./poo/listings/accesseurs1_c.nit}
220
221 Les accesseurs manuels sont tout a fait comme des méthodes et acceptent de ce fait les modificateurs d'accès de visibilité. Pour les accesseurs automatiques, le principe est différent.
222
223 Pour les getters, c'est la visibilité de l'attribut qui va déterminer la visibilité de la méthode :
224 \begin{description}
225 \item[par défaut (public)] Le getter automatique sera généré avec la visibilité public.
226 \item[protected] Le getter automatique sera généré avec la visibilité |protected|.
227 \item[private] Le getter automatique sera généré avec la visibilité |private|.
228 \end{description}
229
230 Voici un exemple d'utilisation des getters automatiques :
231 \lstinputlisting[language=Nit]{./poo/listings/accesseurs2_c.nit}
232
233 Pour les setters, ils sont toujours générés comme étant |private|. Pour changer cette visibilité, il est nécessaire de préciser l'attribut comme |writable| en lui rajoutant la visibilité souhaitée :
234 \begin{description}
235 \item[writable] Le setter automatique sera généré avec la visibilité public.
236 \item[protected writable] Le setter automatique sera généré avec la visibilité |protected|.
237 \item[private writable] Le setter automatique sera généré avec la visibilité |private| (c'est le comportement par défaut).
238 \end{description}
239
240 Voici un exemple d'utilisation des setters automatiques :
241 \lstinputlisting[language=Nit]{./poo/listings/accesseurs3_c.nit}
242
243 \section{Redéfinition d'attributs}
244 Les accesseurs automatiques sont des considérés comme des méthodes normales, ils peuvent donc être hérités et redéfinis. De plus les accesseurs automatiques peuvent être utilisés pour redéfinir des accesseurs manuels. |redef var| permet de préciser une redéfinition du getter alors que |redef writable| permet de redéfinir le setter :
245 \lstinputlisting[language=Nit]{./poo/listings/accesseurs4_c.nit}
246
247 \section{Opérateur isset}
248
249 Pour vérifier si un opérateur a été initialisée, on peut utiliser l'opérateur |isset| :
250 \lstinputlisting[language=Nit,]{./poo/listings/isset_c.nit}
251
252 \chapter{Constructeurs}
253 \label{constructeurs}
254
255 \section{Constructeurs standards}
256 L'instanciation d'un objet est suivie d'une sorte d'initialisation par le moyen d'une méthode particulière appelée constructeur pour que les variables aient une valeur de départ. Elle n'est systématiquement invoquée que lors de la création d'un objet.
257
258 La syntaxe de la déclaration d'une constructeur est :
259 \begin{lstlisting}[language=Nit]
260 [redef] [protected|private] init [nom][( arg1:Type, ... )] do
261 ...
262 end
263 \end{lstlisting}
264
265 Un constructeur ne possède pas de type de retour.
266
267 \lstinputlisting[language=Nit]{./poo/listings/constructeur1_c.nit}
268
269 La définition d'un constructeur est facultative. Si aucun constructeur n'est explicitement défini dans la classe, le compilateur va créer un constructeur par défaut en fonction des attributs définis dans la classe :
270 \lstinputlisting[language=Nit]{./poo/listings/constructeur2_c.nit}
271
272 Bien sûr, il est possible de forcer un constructeur en le déclarant explicitement afin de créer un constructeur différent que celui qui serait généré par défaut:
273 \lstinputlisting[language=Nit]{./poo/listings/constructeur3_c.nit}
274
275 \section{Constructeurs nommés}
276 En Nit, il n'est pas possible de surcharger un constructeur. En revanche, il est possible de nommer les constructeurs :
277 \lstinputlisting[language=Nit]{./poo/listings/constructeur4_c.nit}
278
279 \section{Constructeurs abstraits}
280 Les constructeurs peuvent être déclarés comme abstrait avec la syntaxe suivante :
281 \begin{lstlisting}[language=Nit]
282 [redef] [protected|private] init [nom][( arg1:Type, ... )] is abstract
283 \end{lstlisting}
284
285 \chapter{Héritage}
286 \label{heritage}
287 L'héritage est un mécanisme qui facilite la réutilisation du code et la gestion de son évolution. Elle définit une relation entre deux classes :
288 \begin{itemize}
289 \item une classe mère ou super classe
290 \item une classe fille ou sous classe qui hérite de sa classe mère
291 \end{itemize}
292
293 \section{Principes de l'héritage}
294 Grâce à l'héritage, les objets d'une classe fille ont accès aux données et aux méthodes des classes parentes et peuvent les étendre. Les sous classes peuvent redéfinir les attributs et les méthodes héritées.
295
296 L'héritage successif de classes permet de définir une hiérarchie de classe qui se compose de super classes et de sous classes. Une classe qui hérite d'une autre est une sous classe et celle dont elle hérite est une super classe. Une classe peut avoir plusieurs sous classes et plusieurs classes mères. |Object| est la classe parente de toutes les classes en Nit. Tous les attributs et méthodes contenues dans |Object| sont accessibles à partir de n'importe quelle classe car par héritages successifs toutes les classes héritent d'|Object|.
297
298 \section{Mise en \oe{}uvre}
299 On utilise le mot clé |super| pour indiquer qu'une classe hérite d'une autre. En l'absence de ce mot réservé associé à une classe, le compilateur considère la classe |Object| comme classe mère.
300 \lstinputlisting[language=Nit]{./poo/listings/heritage1_s.nit}
301
302 Pour invoquer une méthode d'une classe parent, il suffit d'utiliser le mot-clé |super|. Les paramètres seront transmis automatiquement :
303 \lstinputlisting[language=Nit]{./poo/listings/super1_c.nit}
304
305 En Nit, il est obligatoire dans un constructeur d'une classe fille de faire appel explicitement ou implicitement au constructeur de la classe mère.
306
307 Si rien n'est précisé alors l'appel sera fait implicitement, le constructeur de la classe mère sera appelé avant le constructeur de la classe fille :
308 \lstinputlisting[language=Nit]{./poo/listings/super2_c.nit}
309
310 Si |super| est utilisé, alors l'appel sera fait à l'endroit précisé :
311 \lstinputlisting[language=Nit, linerange=8-19]{./poo/listings/super3_c.nit}
312
313 \section{Accès aux propriétés héritées}
314 Les variables et méthodes définies avec le modificateur d'accès par défaut (public) restent publiques à travers l'héritage et toutes les autres classes.
315 Une variable d'instance définie avec le modificateur |private| est bien héritée mais elle n'est pas accessible directement mais via les méthodes héritées (mise à part dans le fichier où est définie la classe).
316
317 Si l'on veut conserver pour une propriété une protection semblable à celle assurée par le modificateur |private|, il faut utiliser le modificateur |protected|. La propriété ainsi définie sera héritée dans toutes les classes descendantes qui pourront y accéder en utilisant le mot-clé |self| mais ne sera pas accessible sur tout autre receveur.
318
319 \section{Redéfinition de méthodes}
320 \label{redef}
321 La redéfinition d'une méthode héritée doit impérativement conserver la déclaration de la méthode parent (type et nombre de paramètres et la valeur de retour doivent être identiques). Si la signature de la méthode change, ce n'est plus une redéfinition mais une surcharge (et rappelez-vous : ce n'est pas autorisé en Nit !).
322
323 Pour redéfinir une méthode, il suffit d'utiliser le mot-clé |redef| :
324 \lstinputlisting[language=Nit]{./poo/listings/redef1_c.nit}
325
326 Lors de la redéfinition d'une méthode avec le mot-clé |redef|, il n'est pas nécessaire de préciser à nouveau le prototype de la méthode :
327 \lstinputlisting[language=Nit]{./poo/listings/redef4_c.nit}
328
329 %\section{Redéfinition de constructeurs}
330
331 %\subsubsection{Redéfinition de constructeurs automatiques}
332
333 %Les constructeurs automatiques ou manuel sont transmis de la classe mère à la classe fille par héritage. Il est possible de redéfinir un constructeur automatique sans utiliser le mot-clé |redef| comme ceci :
334 %\lstinputlisting[language=Nit]{./poo/listings/redef5_c.nit}
335
336 \section{Polymorphisme}
337 Le polymorphisme est la capacité, pour un même message de correspondre à plusieurs formes de traitements selon l'objet auquel ce message est adressé. La gestion du polymorphisme est assurée dynamiquement à l'exécution.
338
339 L'idée est de partir d'un type et de le modifier. Par exemple, on peut créer une classe de base, puis faire des classes dérivées :
340 \lstinputlisting[language=Nit]{./poo/listings/polymorphisme_c.nit}
341
342 On peut ensuite traiter les objets de la même manière quelques soit leur type dynamique :
343 \lstinputlisting[language=Nit, linerange=39-43, firstnumber=last]{./poo/listings/polymorphisme_c.nit}
344
345 L'héritage définit un sous-typage implicite de la classe fille vers la classe mère : on peut affecter à une référence d'une classe n'importe quel objet d'une de ses sous classes.
346
347 \section{Coercition}
348 La coercition (conversion de type) peut être utilisé dans le cadre d'un cast standard grâce au mot-clé |as| :
349 \lstinputlisting[language=Nit]{./poo/listings/coercition1_c.nit}
350
351 L'inconvénient de cette méthode est que si l'objet n'avait pas été dynamiquement typé en tant que Vache, une erreur d'exécution serait survenue lors du cast puisque celui-ci aurait échoué.
352
353 Nit propose une méthode plus sûre permettant de changer temporairement le type statique d'une variable au mot-clé |isa| :
354 \lstinputlisting[language=Nit,firstnumber=last]{./poo/listings/coercition2_s.nit}
355
356 \section{Interfaces}
357 Une interface est un ensemble de déclarations de méthodes correspondant un peu à une classe abstraite. C'est une sorte de standard auquel une classe peut répondre. Tous les objets qui se conforment à cette interface (qui implémentent cette interface) possèdent donc les méthodes déclarées dans celle-ci. Plusieurs interfaces peuvent être implémentées par une même classe.
358
359 Les interfaces se comportent donc comme des classes un peu spéciales :
360 \begin{itemize}
361 \item Une interface ne peut étendre qu'une interface ;
362 \item Une interface ne peut pas avoir de constructeur (méthode init) ;
363 \item Une interface ne peut donc pas être instanciée ;
364 \item Une interface ne peut pas avoir d'attributs ;
365 \end{itemize}
366
367 En Nit, il est possible d'implémenter le corps des méthodes directement dans l'interface, celui-ci sera alors transmis par héritage.
368
369 Voici la syntaxe d'une interface :
370 \begin{lstlisting}[language=Nit]
371 interface nom_interface [special interface_mere...]
372 ...
373 end
374 \end{lstlisting}
375
376 Un exemple de déclaration d'une interface :
377 \lstinputlisting[language=Nit]{./poo/listings/interface_c.nit}
378
379 Les interfaces sont ensuite intégrées aux autres classes avec le mot-clé |super| :
380 \lstinputlisting[language=Nit, linerange=5-22, firstnumber=last]{./poo/listings/interface_c.nit}
381
382 \chapter{Types universels}
383 \label{universal}
384
385 Les types universels sont déclarés à l'aide du mot-clé |universal|. Il s'agit d'éléments qui ne peuvent pas être spécialisés, qui ne peuvent spécialiser que des interfaces, n'ont pas d'attributs et pas de constructeurs.
386
387 Les types universels peuvent avoir des instances mais elles ne sont pas initialisées par le développeur, c'est à dire qu'il est impossible d'utiliser le mot-clé |new| avec un type universel.
388
389 Int et Bool sont deux exemples de types universels.
390
391 \chapter{Généricité et types virtuels}
392
393 \section{Généricité}
394 Le principe de la généricité est de factoriser les méthodes pouvant s'appliquer à n'importe quelle variable quelque soit sont type en évitant les problèmes de coercition.
395
396 \subsection{Généricité simple}
397 Prenons un exemple de classe non générique :
398 \lstinputlisting[language=Nit]{./poo/listings/gen1_c.nit}
399
400 Si nous souhaitons pouvoir utiliser cette classe avec d'autres objets que des |String|, il serait possible de se baser sur le type le plus haut dans la hiérarchie Nit, c'est à dire |Object| :
401 \lstinputlisting[language=Nit, linerange=1-3]{./poo/listings/gen2_c.nit}
402
403 Voyons maintenant ce qu'il se passe quand nous utilisons cette classe avec des |Int| :
404 \lstinputlisting[language=Nit, linerange=5-7, firstnumber=last]{./poo/listings/gen2_c.nit}
405
406 Vous remarquerez qu'il est nécessaire d'utiliser un cast pour utiliser le retour du |getter| de l'attribut |valeur| puisque celui-ci est de type |Object|. Ce n'est pas pratique et potentiellement dangereux pour la stabilité du programme puisque le risque d'erreur d'exécution sur un cast qui a échoué augmente.
407
408 Dans ce cas, la généricité apporte une solution fiable et robuste. Elle permet de paramétrer des méthodes avec un type de données joker qui sera résolu dynamiquement au moment de l'instanciation de l'objet.
409
410 Voici le code de la classe déclarée comme générique :
411 \lstinputlisting[language=Nit, linerange=1-3]{./poo/listings/gen3_c.nit}
412
413 Dans cette classe, le T n'est pas encore défini, cela se fera à l'instanciation. Par contre, une fois instancié avec un type, l'objet ne pourra travailler qu'avec le type de données spécifié à l'instanciation.
414
415 La déclaration d'une classe générique se fait en utilisant précisant le type joker (représenté par n'importe quel mot) entre crochet |[T]|. Il suffit ensuite d'implémenter les méthodes comme nous le ferions habituellement mais en remplaçant les types par le joker que nous avons défini dans la déclaration de la classe.
416
417 Voyons comment utiliser notre classe générique avec des entiers :
418 \lstinputlisting[language=Nit, linerange=5-6, firstnumber=last]{./poo/listings/gen3_c.nit}
419
420 Ou encore avec des chaînes de caractères :
421 \lstinputlisting[language=Nit, linerange=8-9, firstnumber=last]{./poo/listings/gen3_c.nit}
422
423 \subsection{Généricité bornée}
424 Dans certains cas, il peut être utile de limiter la portée de la généricité à un certain nombre de types ayant la même intention. Pour cela nous pouvons utiliser l'héritage afin de n'autoriser que les sous-types d'une certaine classe. C'est le principe de la généricité bornée.
425
426 Imaginons cette fois que la classe Solo n'a de sens que si elle est utilisée avec un objet de type Animal ou l'un de ses sous-type. Nous allons pouvoir préciser la borne sur notre joker dans la définition de la classe générique :
427 \lstinputlisting[language=Nit, linerange=1-3]{./poo/listings/gen4_s.nit}
428
429 Il n'est maintenant plus possible d'utiliser cette classe avec autre chose que le type Animal ou l'un de ses sous-type.
430
431 En réalité, toute classe utilisant la généricité sans borne apparente est bornée implicitement sur |[T: nullable Object]|.
432
433 \subsection{Généricité et héritage}
434 Nit, contrairement à d'autres langages, supporte la covariance des variables dans le cas des classes génériques.
435
436 L'exemple suivant est donc tout à fait valable en Nit :
437 \lstinputlisting[language=Nit, linerange=5-8, firstnumber=last]{./poo/listings/gen4_s.nit}
438
439 \subsection{Généricité multiple}
440 Nit supporte aussi la généricité multiple comme le montre l'exemple suivant :
441 \lstinputlisting[language=Nit]{./poo/listings/gen5_c.nit}
442
443 \section{Types virtuels}
444 Les types virtuels permettent définir au sein d'une classe un champ qui va contenir un type plutôt d'un objet. Ce champ peut ensuite être utilisé pour typer les paramètres et les types de retour des méthodes. Sa valeur sera exploitée à la compilation pour définir quel est le type à utiliser.
445
446 Exemple d'utilisation d'un type virtuel :
447 \lstinputlisting[language=Nit]{./poo/listings/type1_c.nit}
448
449 A quoi peuvent servir les types virtuels ?
450
451 Prenons un exemple très simplifié qui n'utilise pas les types virtuels :
452 \lstinputlisting[language=Nit]{./poo/listings/type2_c.nit}
453
454 Nous souhaitons maintenant étendre cette classe pour représenter un XMLDocument comprenant des XMLNode :
455 \lstinputlisting[language=Nit,firstnumber=last]{./poo/listings/type3_s.nit}
456
457 Jusque là tout va bien. Essayons maintenant de manipuler cette classe :
458 \lstinputlisting[language=Nit,firstnumber=last]{./poo/listings/type4_s.nit}
459
460 Pour forcer l'utilisation de la méthode add\_node de XMLDocument avec un paramètre de type XMLNode, nous voilà obligé de redéfinir entièrement la classe Node :
461 \lstinputlisting[language=Nit]{./poo/listings/type5_s.nit}
462
463 Nous aurions pu éviter cela en utilisant les types virtuels :
464 \lstinputlisting[language=Nit]{./poo/listings/type6_s.nit}
465
466 \chapter{Modules}
467 En Nit, il existe un moyen de regrouper des classes voisines ou qui couvrent un même domaine : ce sont les modules.
468
469 \section{Définition d'un module}
470 En Nit, la convention est de regrouper les classes qui doivent faire partie du même module dans un même fichier. Le nom du module doit correspondre au nom du fichier sans l'extension .nit.
471
472 Pour spécifier le nom du moule, il suffit de rajouter la directive |module nom_du_module| au début du fichier :
473 \lstinputlisting[language=Nit]{./poo/listings/package1_c.nit}
474
475 Le mot-clé |module| doit être la première instruction dans un fichier source et il ne doit être présent qu'une seule fois dans le fichier source (une classe ne peut pas appartenir à plusieurs modules).
476
477 \section{Utilisation d'un module}
478
479 \subsection{Import standard}
480 Pour utiliser ensuite le module ainsi créé, on l'importe dans le fichier grâce à l'instruction :
481 \begin{lstlisting}[language=Nit]
482 [intrude|private] import nom_du_module
483 \end{lstlisting}
484 \lstinputlisting[language=Nit]{./poo/listings/import1_c.nit}
485
486 Si le nom du module diffère du nom du fichier, le compilateur ne sera pas capable de lier le module est retournera une erreur à la compilation : no ressource found for module nom\_du\_module.
487
488 Pour l'instant il n'est pas possible d'importer des modules se trouvant dans un autre répertoire que celui où se trouvent les fichiers sources.
489
490 \subsection{Import privé}
491 Si on utilise le mot-clé |private| devant le |import|, le module sera importé dans le module courant comme privé. C'est à dire que tous les modules qui importeront le module courant ne verrons pas les classes et les propriétés importées.
492
493 \subsection{Import intrusif}
494 L'utilisation du mot-clé |intrude| avant l'import d'un module permet d'importer le module en mode intrusif, c'est à dire d'ignorer toutes les restrictions liées à la visibilité. Ainsi les méthodes déclarées |private| seront considérées comme publiques.
495
496 Cette fonctionnalité est à utiliser avec beaucoup de précautions, elle est même déconseillée dans la plupart des cas.
497
498 \section{Raffinement de classe}
499 \label{raffinement-classe}
500 Nit permet de redéfinir des classes depuis une autre module pour en modifier les méthodes ou en rajouter, c'est le raffinement de classe.
501
502 Pour modifier une classe déjà déclarée il faut utiliser le mot-clé |redef| :
503 \lstinputlisting[language=Nit]{./poo/listings/redef2_c.nit}
504
505 Ici, nous venons de raffiner la classe Object pour lui ajouter la méthode coucou. Maintenant Object et toutes ses sous classes possèdent la méthode coucou. Par exemple nous appelons la méthode coucou sur l'objet de type Int.
506
507 Il est aussi possible de redéfinir les méthodes déjà existantes :
508 \lstinputlisting[language=Nit]{./poo/listings/redef3_c.nit}
509
510 Dans cet exemple nous avons redéfini la méthode to\_s de la classe String afin de retourner toutes les chaines en majuscules.
511
512 \chapter{Importation de la bibliothèque standard}
513 Il faut noter que les modules de la bibliothèque standard sont toujours importés implicitement lors de la compilation si aucun import n'a été spécifié. Par exemple il n'est pas nécessaire d'importer le module string pour utiliser la classe String.