Author: Christian.Queinnec@lip6.fr

## Classes, objets et méthodes

L'extension objet d'Objective CAML s'intègre au noyau fonctionnel et au noyau impératif du langage, mais aussi à son système de types. C'est ce dernier point qui en fait son originalité. On obtient ainsi un langage objet, typé statiquement, avec inférence de types. Cette extension permet de définir des classes et des instances, autorise l'héritage entre classes (y compris l'héritage multiple), accepte les classes paramétrées et les classes abstraites. Les interfaces de classes sont engendrées par leur définition mais peuvent être précisées par une signature, à l'instar de ce qui est fait pour les modules.

### Terminologie objet

Nous décrivons brièvement dans ce paragraphe les principaux éléments du glossaire de la programmation par objet.

classe
  une classe est un ensemble agrégé de champs de données (appelés des variables d'instance) et de traitements (appelés méthodes).
objet
  un objet est un élément (ou instance) d'une classe. Un objet possède les comportements de la classe à laquelle il appartient. L'objet est le composant effectif des programmes (c'est lui qui calcule) alors que la classe est plutôt une définition ou une spécification pour l'ensemble des instances à venir.
méthode
  une méthode est une action qu'un objet est à même d'effectuer.
envoi de message
  un envoi de message à un objet est la demande faite à ce dernier d'exécuter une de ses méthodes. On pourra également dire que l'on invoque une méthode.

### Déclaration d'une classe

La syntaxe la plus simple pour définir une classe est la suivante. Nous enrichirons cette définition tout au long du chapitre.

### Syntaxe

class nom p1 ...pn =
object
:
variables d'instance
:
méthodes
:
end

p1, ..., pn sont les paramètres que prendra le constructeur de cette classe. Une classe peut n'avoir aucun paramètre.
Une variable d'instance se déclare par :

### Syntaxe

val nom = expr
ou
val mutable nom = expr

Dans la mesure où un champ de données est déclaré mutable, il est possible d'en modifier sa valeur. Dans le cas contraire, la valeur est celle calculée à la création de l'objet par l'évaluation de expr.
Une méthode se déclare par :

### Syntaxe

method nom p1 ...pn = expr

Il existe d'autres clauses que val et method dans la déclaration d'une classe. Nous les introduirons au fur et à mesure des besoins.

##### Une première classe

Commençons par l'inévitable classe `point`; elle contient :

- deux champs de données `x` et `y` contenant les coordonnées du point,
- six méthodes :
  - deux méthodes d'accès aux champs de données (`get_x` et `get_y`),
  - deux procédures de déplacement absolu (`moveto`) et relatif (`rmoveto`),
  - une méthode de présentation des données sous forme de string (`to_string`),
  - une méthode de calcul de la distance du point par rapport à l'origine (`distance`).

    # class point (x_init,y_init) =
       object
         val mutable x = x_init
         val mutable y = y_init
         method get_x = x
         method get_y = y
         method moveto (a,b) =  x <- a ; y <- b
         method rmoveto (dx,dy) =  x <- x + dx ; y <- y + dy
         method to_string () =
             "( " ^ (string_of_int x) ^ ", " ^ (string_of_int y) ^")"
         method distance () = sqrt (float(x*x + y*y))
       end ;;

Remarquons que les méthodes peuvent ne pas avoir de paramètre. C'est le cas de `get_x` et de `get_y`. Nous prendrons comme convention de définir les accesseurs aux variables d'instance par des méthodes sans paramètre.
Après la déclaration de la classe `point`, le système affiche le texte suivant :

    class point :
      int * int ->
      object
        val mutable x : int
        val mutable y : int
        method distance : unit -> float
        method get_x : int
        method get_y : int
        method moveto : int * int -> unit
        method rmoveto : int * int -> unit
        method to_string : unit -> string
      end

Ce texte contient deux informations. D'une part, un type pour les objets de cette classe. Ce type sera abrégé par le nom point. Le type d'un objet est la liste des noms des méthodes de sa classe avec leur type. Dans notre exemple, point est une abréviation pour :

      < distance : unit -> unit; get_x : int; get_y : int;
        moveto : int * int -> unit; rmoveto : int * int -> unit;
        to_string : unit -> unit >

D'autre part, un constructeur d'instances de la classe `point`, de type int*int -> oint, qui permet de construire un objet `point` à partir des valeurs initiales fournies en argument. Ici, on construit un `point` à partir d'un couple d'entiers (sa position initiale). La fonction de construction `point` sera utilisée avec le mot réservé new.
Il est possible de définir des types de classe :

    # type simple_point = < get_x : int; get_y : int; to_string : unit -> unit > ;;
    type simple_point = < get_x : int; get_y : int; to_string : unit -> unit >

### Remarque

Le type point ne reprend pas la totalité des informations affichées après la déclaration de la classe. Les variables d'instance n'apparaissent pas dans le type. On ne peut accéder à ces valeurs que par l'entremise d'une méthode.

### Warning

Une déclaration de classe est considérée comme une déclaration de type. Elle ne peut donc pas contenir de variable de type libre.

Nous reviendrons sur ce point quand nous aborderons les contraintes de type (page ??) et les classes paramétrées (page ??).

#### Notation graphique des classes

On adapte la notation UML à la syntaxe des types d'Objective CAML. Les classes se notent par un rectangle constitué de trois parties :

- une partie portant le nom de la classe,
- une autre où figurent les attributs (champs de données) d'une instance de la classe,
- une dernière où sont inscrites les méthodes d'une instance de la classe.

La figure 15.1 donne un exemple de représentation graphique de la classe `chameau`.

> Figure 15.1 : représentation graphique d'une classe

On peut ajouter l'information de types pour les champs d'une classe.

### Création d'une instance

Un objet est une valeur d'une classe, appelée instance de cette classe. Cette instance est créée par la primitive de construction générique `new` à laquelle on indique la classe et les valeurs d'initialisation.

### Syntaxe

new nom espr1 ...exprn

L'exemple suivant construit plusieurs instances de la classe point, à partir de valeurs initiales différentes.

    # let p1 = new point (0,0);;
    val p1 : point = <obj>
    # let p2 = new point (3,4);;
    val p2 : point = <obj>
    # let coord = (3,0);;
    val coord : int * int = 3, 0
    # let p3 = new point coord;;
    val p3 : point = <obj>

En Objective CAML, le constructeur d'une classe est unique mais rien ne nous empêche de déclarer une fonction spécifique `instancie_point` de création de points :

    # let instancie_point x = new point (x,x) ;;
    val instancie_point : int -> point = <fun>
    # instancie_point 1 ;;
    - : point = <obj>

### Envoi d'un message

L'envoi d'un message à un objet s'effectue par la notation `#` 2.

### Syntaxe

nom1#nom2 p1 ...pn

Le message (la méthode) nom2 est envoyé à l'objet nom1. Les arguments p1, ..., pn sont ceux attendus par la méthode nom2. Elle doit être connue dans la classe de l'objet, c'est-à-dire visible dans son type. Les types des paramètres d'appel doivent satisfaire les types des paramètres formels. L'exemple suivant montre différentes requêtes effectuées sur des objets de la classe `point`.

    # p1#get_x;;
    - : int = 0
    # p2#get_y;;
    - : int = 4
    # p1#to_string();;
    - : string = "( 0, 0)"
    # p2#to_string();;
    - : string = "( 3, 4)"
    # if (p1#distance()) = (p2#distance())
     then print_string ("c'est le hasard\n")
     else print_string ("on pouvait parier\n");;
    on pouvait parier
    - : unit = ()

Du point de vue des types, les objets de type point peuvent être manipulés par les fonctions polymorphes d'Objective CAML comme n'importe quelle valeur du langage :

    # p1 = p1 ;;
    - : bool = true
    # p1 = p2;;
    - : bool = false
    # let l = p1::[];;
    val l : point list = [<obj>]
    # List.hd l;;
    - : point = <obj>

### Warning

L'égalité entre deux objets est vérifiée uniquement dans le cas où ils sont physiquement identiques.

Ce point sera détaillé lors de l'étude de la relation de sous-typage (page ??).