doc: minor corrections on "developpez 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 ensemble d'objet ayant des propriétés communes. 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.
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 de 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èrent 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'est pas nécessaire 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 pour 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 un objet de 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 |==| est celui de la classe |Object| et 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éfini 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 il faut 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-6, firstnumber=last]{./poo/listings/comparaison2_c.nit}
78
79 \section{Types nullable}
80 En Nit, il existe une particularité pour la valeur |null|. Contrairement à Java il ne peut pas être utilisé 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 La valeur |null| peut donc être utilisée avec n'importe quel type \textit{nullable} mais aucun message ne peut être envoyé à |null|.
87
88 \chapter{Modificateurs d'accès}
89 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.
90
91 \section{Visibilité des entités}
92 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.
93
94 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) :
95
96 %TODO reformuler
97 \begin{description}
98 \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 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.
99 \item[protected] Si un attribut ou une méthode est déclaré |protected|, seules les méthodes des sous classes pourront y accéder.
100 \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.
101 \end{description}
102
103 Ces modificateurs d'accès sont mutuellement exclusifs.
104
105 \section{Classes abstraites}
106 \label{classes-abstraites}
107
108 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.
109
110 \lstinputlisting[language=Nit]{./poo/listings/abstract1_c.nit}
111
112 Essayer d'instancier une classe abstraite générera une erreur de compilation.
113
114 \chapter{Méthodes}
115 Les méthodes sont des fonctions qui implémentent les traitements de la classe.
116
117 \section{Syntaxe et déclaration}
118 La syntaxe de la déclaration d'une méthode est :
119 \begin{lstlisting}[language=Nit]
120 [redef] [protected|private] fun nom_de_méthode[( arg1:Type, ... )][:Type_retourné] do
121 ...
122 end
123 \end{lstlisting}
124
125 \section{Types retournés}
126 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é.
127
128 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 : les instructions qui suivent |return| à l'intérieur du même bloc 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.
129
130 \lstinputlisting[language=Nit]{./poo/listings/methode1_c.nit}
131
132 Il est possible d'inclure une instruction |return| dans une méthode sensée ne rien retourner : cela permet de quitter la méthode.
133
134 \section{Passage de paramètres}
135 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 une méthode n'accepte pas de paramètres, il suffit de ne pas indiquer de parenthèses :
136 \lstinputlisting[language=Nit]{./poo/listings/methode2_c.nit}
137
138 Si plusieurs paramètres ont le même type, il est possible de le factoriser :
139 \lstinputlisting[language=Nit]{./poo/listings/methode7_c.nit}
140
141 Il n'est pas possible d'indiquer des valeurs par défaut dans les paramètres.
142
143 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. 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.
144
145 \section{Envoi de message}
146 Un message est émis lorsqu'on demande à un objet d'exécuter l'une de ses méthodes.
147 La syntaxe d'appel d'une méthode est : |objet.methode(paramètre, ...)|
148
149 Si la méthode appelée ne contient aucun paramètre, il n'est pas nécessaire de mettre les parenthèses.
150
151 \lstinputlisting[language=Nit]{./poo/listings/methode4_s.nit}
152
153 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 :
154 \lstinputlisting[language=Nit]{./poo/listings/methode8_c.nit}
155
156 \section{Nombre variable de paramètres}
157 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 zéro, un ou plusieurs arguments pour le paramètre ainsi déclaré.
158
159 \lstinputlisting[language=Nit]{./poo/listings/methode9_c.nit}
160
161 Un seul paramètre variable est autorisé dans une signature.
162
163 \section{Enchaînement de références}
164 Il est possible d'enchaîner les envois de messages aux retours des méthodes :
165 \lstinputlisting[language=Nit]{./poo/listings/methode5_c.nit}
166
167 Ici on appelle la méthode \textit{to\_upper} de l'objet retourné par la méthode \textit{substring} de l'objet \textit{"abcd"}.
168
169 \section{Mot-clé self}
170
171 Le mot-clé |self| est l'équivalent du |this| en Java. Il permet de pointer sur le receveur courant :
172 \lstinputlisting[language=Nit]{./poo/listings/self_c.nit}
173
174 \section{Redéfinition d'opérateurs}
175 \label{redef-operateur}
176
177 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 :
178 \lstinputlisting[language=Nit]{./poo/listings/methode6_c.nit}
179
180 La seule restriction est que la méthode doit forcément prendre un et un seul paramètre et retourner quelque chose.
181
182 \section{Surcharge statique de méthode}
183 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.
184
185 \section{Méthodes abstraites}
186 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.
187
188 La syntaxe de la déclaration d'une méthode abstraite est :
189 \begin{lstlisting}[language=Nit]
190 [redef] [protected|private] fun nom_de_méthode[( arg1:Type, ... )][:Type_retourné] is abstract
191 \end{lstlisting}
192
193 \lstinputlisting[language=Nit, linerange=1-3]{./poo/listings/abstract2_c.nit}
194
195 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}) :
196 \lstinputlisting[language=Nit, linerange=5-11, firstnumber=last]{./poo/listings/abstract2_c.nit}
197
198 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}).
199
200 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.
201
202 \chapter{Attributs}
203 Les données d'une classe sont contenues dans des variables nommées attributs.
204
205 \section{Déclaration des attributs}
206 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.
207
208 \lstinputlisting[language=Nit]{./poo/listings/attribut1_c.nit}
209
210 Chaque instance de la classe a accès à sa propre occurrence de l'attribut.
211
212 Il est possible de définir des valeurs par défaut pour chaque attribut :
213 \lstinputlisting[language=Nit]{./poo/listings/attribut2_c.nit}
214
215 \section{Accesseurs}
216 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.
217
218 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).
219
220 Il reste tout de même possible de définir ses propres accesseurs :
221 \lstinputlisting[language=Nit]{./poo/listings/accesseurs1_c.nit}
222
223 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.
224
225 Pour les getters, c'est la visibilité de l'attribut qui va déterminer la visibilité de la méthode :
226 \begin{description}
227 \item[par défaut (public)] Le getter automatique sera généré avec la visibilité public.
228 \item[protected] Le getter automatique sera généré avec la visibilité |protected|.
229 \item[private] Le getter automatique sera généré avec la visibilité |private|.
230 \end{description}
231
232 Voici un exemple d'utilisation des getters automatiques :
233 \lstinputlisting[language=Nit]{./poo/listings/accesseurs2_c.nit}
234
235 Pour les setters, ils sont toujours générer 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 :
236 \begin{description}
237 \item[writable] Le setter automatique sera généré avec la visibilité public.
238 \item[protected writable] Le setter automatique sera généré avec la visibilité |protected|.
239 \item[private writable] Le setter automatique sera généré avec la visibilité |private| (c'est le comportement par défaut).
240 \end{description}
241
242 Voici un exemple d'utilisation des setters automatiques :
243 \lstinputlisting[language=Nit]{./poo/listings/accesseurs3_c.nit}
244
245 \section{Redéfinition d'attributs}
246 Les accesseurs automatiques sont 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 :
247 \lstinputlisting[language=Nit]{./poo/listings/accesseurs4_c.nit}
248
249 \section{Opérateur isset}
250
251 Pour vérifier si un attribut a été initialisé, on peut utiliser l'opérateur |isset| :
252 \lstinputlisting[language=Nit,]{./poo/listings/isset_c.nit}
253
254 \chapter{Constructeurs}
255 \label{constructeurs}
256
257 \section{Constructeurs standards}
258 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.
259
260 La syntaxe de la déclaration d'une constructeur est :
261 \begin{lstlisting}[language=Nit]
262 [redef] [protected|private] init [nom][( arg1:Type, ... )] do
263 ...
264 end
265 \end{lstlisting}
266
267 Un constructeur ne possède pas de type de retour.
268
269 \lstinputlisting[language=Nit]{./poo/listings/constructeur1_c.nit}
270
271 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 :
272 \lstinputlisting[language=Nit]{./poo/listings/constructeur2_c.nit}
273
274 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:
275 \lstinputlisting[language=Nit]{./poo/listings/constructeur3_c.nit}
276
277 \section{Constructeurs nommés}
278 En Nit, il n'est pas possible de surcharger un constructeur. En revanche, il est possible de nommer les constructeurs :
279 \lstinputlisting[language=Nit]{./poo/listings/constructeur4_c.nit}
280
281 \section{Constructeurs abstraits}
282 Les constructeurs peuvent être déclarés comme abstrait avec la syntaxe suivante :
283 \begin{lstlisting}[language=Nit]
284 [redef] [protected|private] init [nom][( arg1:Type, ... )] is abstract
285 \end{lstlisting}
286
287 \chapter{Héritage}
288 \label{heritage}
289 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 :
290 \begin{itemize}
291 \item une classe mère ou super classe
292 \item une classe fille ou sous classe qui hérite de sa classe mère
293 \end{itemize}
294
295 \section{Principes de l'héritage}
296 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.
297
298 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|.
299
300 \section{Mise en \oe{}uvre}
301 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.
302 \lstinputlisting[language=Nit]{./poo/listings/heritage1_s.nit}
303
304 Pour invoquer une méthode d'une classe parent, il suffit d'utiliser le mot-clé |super|. Les paramètres seront transmis automatiquement :
305 \lstinputlisting[language=Nit]{./poo/listings/super1_c.nit}
306
307 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.
308
309 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 :
310 \lstinputlisting[language=Nit]{./poo/listings/super2_c.nit}
311
312 Si |super| est utilisé, alors l'appel sera fait à l'endroit précisé :
313 \lstinputlisting[language=Nit, linerange=8-19]{./poo/listings/super3_c.nit}
314
315 \section{Accès aux propriétés héritées}
316 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.
317 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).
318
319 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.
320
321 \section{Redéfinition de méthodes}
322 \label{redef}
323 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.
324
325 Pour redéfinir une méthode, il suffit d'utiliser le mot-clé |redef| :
326 \lstinputlisting[language=Nit]{./poo/listings/redef1_c.nit}
327
328 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 :
329 \lstinputlisting[language=Nit]{./poo/listings/redef4_c.nit}
330
331 %\section{Redéfinition de constructeurs}
332
333 %\subsubsection{Redéfinition de constructeurs automatiques}
334
335 %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 :
336 %\lstinputlisting[language=Nit]{./poo/listings/redef5_c.nit}
337
338 \section{Polymorphisme}
339 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.
340
341 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 :
342 \lstinputlisting[language=Nit, linerange=1-35]{./poo/listings/polymorphisme_c.nit}
343
344 On peut ensuite traiter les objets de la même manière quelques soit leur type dynamique :
345 \lstinputlisting[language=Nit, linerange=37-43, firstnumber=last]{./poo/listings/polymorphisme_c.nit}
346
347 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.
348
349 \section{Coercition}
350 La coercition (conversion de type) peut être utilisée dans le cadre d'un cast standard grâce au mot-clé |as| :
351 \lstinputlisting[language=Nit]{./poo/listings/coercition1_c.nit}
352
353 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é.
354
355 Nit propose une méthode plus sûre permettant de changer temporairement le type statique d'une variable au mot-clé |isa| :
356 \lstinputlisting[language=Nit,firstnumber=last]{./poo/listings/coercition2_s.nit}
357
358 \section{Interfaces}
359 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.
360
361 Les interfaces se comportent donc comme des classes un peu spéciales :
362 \begin{itemize}
363 \item Une interface ne peut étendre qu'une interface ;
364 \item Une interface ne peut pas avoir de constructeur (méthode init) ;
365 \item Une interface ne peut donc pas être instanciée ;
366 \item Une interface ne peut pas avoir d'attributs ;
367 \end{itemize}
368
369 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.
370
371 Voici la syntaxe d'une interface :
372 \begin{lstlisting}[language=Nit]
373 interface nom_interface [special interface_mere...]
374 ...
375 end
376 \end{lstlisting}
377
378 Un exemple de déclaration d'une interface :
379 \lstinputlisting[language=Nit]{./poo/listings/interface_c.nit}
380
381 Les interfaces sont ensuite intégrées aux autres classes avec le mot-clé |super| :
382 \lstinputlisting[language=Nit, linerange=5-22, firstnumber=last]{./poo/listings/interface_c.nit}
383
384 \chapter{Types universels}
385 \label{universal}
386
387 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.
388
389 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.
390
391 Int et Bool sont deux exemples de types universels.
392
393 \chapter{Généricité et types virtuels}
394
395 \section{Généricité}
396 Le principe de la généricité est de factoriser les méthodes pouvant s'appliquer à n'importe quelle variable quel que soit son type en évitant les problèmes de coercition.
397
398 \subsection{Généricité simple}
399 Prenons un exemple de classe non générique :
400 \lstinputlisting[language=Nit]{./poo/listings/gen1_c.nit}
401
402 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| :
403 \lstinputlisting[language=Nit, linerange=1-3]{./poo/listings/gen2_c.nit}
404
405 Voyons maintenant ce qui se passe quand nous utilisons cette classe avec des |Int| :
406 \lstinputlisting[language=Nit, linerange=5-7, firstnumber=last]{./poo/listings/gen2_c.nit}
407
408 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.
409
410 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.
411
412 Voici le code de la classe déclarée comme générique :
413 \lstinputlisting[language=Nit, linerange=1-3]{./poo/listings/gen3_c.nit}
414
415 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.
416
417 La déclaration d'une classe générique se fait en 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.
418
419 Voyons comment utiliser notre classe générique avec des entiers :
420 \lstinputlisting[language=Nit, linerange=5-6, firstnumber=last]{./poo/listings/gen3_c.nit}
421
422 Ou encore avec des chaînes de caractères :
423 \lstinputlisting[language=Nit, linerange=8-9, firstnumber=last]{./poo/listings/gen3_c.nit}
424
425 \subsection{Généricité bornée}
426 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.
427
428 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 :
429 \lstinputlisting[language=Nit, linerange=1-3]{./poo/listings/gen4_s.nit}
430
431 Il n'est maintenant plus possible d'utiliser cette classe avec autre chose que le type Animal ou l'un de ses sous-type.
432
433 En réalité, toute classe utilisant la généricité sans borne apparente est bornée implicitement sur |[T: nullable Object]|.
434
435 \subsection{Généricité et héritage}
436 Nit, contrairement à d'autres langages, supporte la covariance des variables dans le cas des classes génériques.
437
438 L'exemple suivant est donc tout à fait valable en Nit :
439 \lstinputlisting[language=Nit, linerange=5-8, firstnumber=last]{./poo/listings/gen4_s.nit}
440
441 \subsection{Généricité multiple}
442 Nit supporte aussi la généricité multiple comme le montre l'exemple suivant :
443 \lstinputlisting[language=Nit]{./poo/listings/gen5_c.nit}
444
445 \section{Types virtuels}
446 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 le type à utiliser.
447
448 Exemple d'utilisation d'un type virtuel :
449 \lstinputlisting[language=Nit]{./poo/listings/type1_c.nit}
450
451 A quoi peuvent servir les types virtuels ?
452
453 Prenons un exemple très simplifié qui n'utilise pas les types virtuels :
454 \lstinputlisting[language=Nit]{./poo/listings/type2_c.nit}
455
456 Nous souhaitons maintenant étendre cette classe pour représenter un XMLDocument comprenant des XMLNode :
457 \lstinputlisting[language=Nit,firstnumber=last]{./poo/listings/type3_s.nit}
458
459 Jusque là tout va bien. Essayons maintenant de manipuler cette classe :
460 \lstinputlisting[language=Nit,firstnumber=last]{./poo/listings/type4_s.nit}
461
462 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 :
463 \lstinputlisting[language=Nit]{./poo/listings/type5_s.nit}
464
465 Nous aurions pu éviter cela en utilisant les types virtuels :
466 \lstinputlisting[language=Nit]{./poo/listings/type6_s.nit}
467
468 \chapter{Modules}
469 En Nit, il existe un moyen de regrouper des classes voisines ou qui couvrent un même domaine : ce sont les modules.
470
471 \section{Définition d'un module}
472 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.
473
474 Pour spécifier le nom du moule, il suffit de rajouter la directive |module nom_du_module| au début du fichier :
475 \lstinputlisting[language=Nit]{./poo/listings/package1_c.nit}
476
477 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).
478
479 \section{Utilisation d'un module}
480
481 \subsection{Import standard}
482 Pour utiliser ensuite le module ainsi créé, on l'importe dans le fichier grâce à l'instruction :
483 \begin{lstlisting}[language=Nit]
484 [intrude|private] import nom_du_module
485 \end{lstlisting}
486 \lstinputlisting[language=Nit]{./poo/listings/import1_c.nit}
487
488 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.
489
490 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.
491
492 \subsection{Import privé}
493 Si on utilise le mot-clé |private| devant le |import|, le module sera importé dans le module courant comme privé. C'est à dire que les modules qui importeront le module courant ne verrons pas les classes et les propriétés importées.
494
495 \subsection{Import intrusif}
496 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.
497
498 Cette fonctionnalité est à utiliser avec beaucoup de précautions, elle est même déconseillée dans la plupart des cas.
499
500 \section{Raffinement de classe}
501 \label{raffinement-classe}
502 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.
503
504 Pour modifier une classe déjà déclarée il faut utiliser le mot-clé |redef| :
505 \lstinputlisting[language=Nit]{./poo/listings/redef2_c.nit}
506
507 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.
508
509 Il est aussi possible de redéfinir les méthodes déjà existantes :
510 \lstinputlisting[language=Nit]{./poo/listings/redef3_c.nit}
511
512 Dans cet exemple nous avons redéfini la méthode to\_s de la classe String afin de retourner toutes les chaines en majuscules.
513
514 \chapter{Importation de la bibliothèque standard}
515 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.