ENPC - Programmation - Séance 5

La programmation orientée objet

La programmation objet, c'est à dire celle qu'on est sensé mettre en oeuvre lorsqu'on écrit des programmes avec un langage à objet (ou langage orienté objet) tel que Java, fait intervenir différents concepts tels que celui de classe, d'objet, de responsabilité, d'encapsulation, etc.
Le premier concept auquel on est confronté lorsqu'on écrit un programme est celui de classe: le mot-clé class, dans

class MaClasse { ... }
permet de définir (entre les accolades) les membres de la classe MaClasse. Les membres sont principalement les méthodes (quelques fois appelées fonctions) et les champs (aussi appelés attributs).

La classe Point

Nous considérons ici la classe Point des objets qui sont des points dans un plan à deux dimensions. Les instances de cette classe disposent donc, pour décrire leur état, de deux champs, x et y, de type double.

Pour créer un point, on dispose d'un constructeur qui accepte les deux coordonnées du point que l'on veut créer.

Un constructeur est une méthode un peu particulière puisqu'il doit avoir exactement le nom de la classe (avec sa majuscule) et qu'il n'a pas de type de retour (en effet, lorsqu'un tel constructeur est associé à l'opérateur new, il retourne la référence de l'objet (l'instance) qui vient d'être alloué et celle-ci est donc typée Point dans notre exemple.

Par ailleurs, on souhaite pouvoir effectuer deux opérations sur n'importe quel point:

Le fichier Point.java accessible à l'URL http://www-igm.univ-mlv.fr/~duris/ENPC/Point.java contient le code initial de cette classe. Récupérez-le, sauvegardez-le et testez-le.

Autour des constructeurs

  1. Modifier les paramètres formels du constructeur pour leur donner les noms x et y au lieu de leX et leY. Quel est le problème?
    Dans une classe, le mot clé this représente la référence à l'instance courante de cette classe. L'utilisation de ce mot-clé est donc interdite dans toute portion de code statique.
  2. Mettre le constructeur et le code du main en commentaire. Dans le main, créer un seul point par Point p = new Point(); et l'afficher. Que s'est il passé? Enlever les commentaires du constructeur et tester à nouveau. Quel est le problème?
    Lorsqu'aucun constructeur n'est explicitement défini, le compilateur crée un constructeur implicite également appelé constructeur par défaut. Si un constructeur explicite est défini, ce constructeur par défaut n'est plus accessible.
  3. Faire en sorte que les deux constructeurs Point(double x, double y) et Point() cohabitent dans la classe Point.
    On dit que ces deux constructeurs sont surchargés: ils ont le même nom mais sont différentiables par la liste de leurs paramètres.

Autour de toString()

  1. Mettre en commentaire la définition de la méthode toString() de la classe Point, recompiler et exécuter. Comment expliquer ce comportement?
    La méthode toString() définie dans la classe Point est une redéfinition de la méthode toString() de la classe Object. En l'absence de la première, c'est la méthode de Object, héritée dans Point, qui est utilisée. Par défaut, elle affiche le nom de la classe de l'objet représenté par la référence et une valeur hexadécimale qui est un code représentant cet objet (hashcode).
  2. Après avoir décommenté la méthode toString() dans le programme, remplacer dans le main l'instruction System.out.println(p.toString()); par System.out.println(p);. Que se passe-t-il?
    Lorsque son argument n'est pas un type primitif, mais une référence à un objet d'une classe, la méthode println appelle automatiquement la méthode toString() sur cette référence avant d'effectuer l'affichage de la chaîne de caractère résultant de cet appel.

Autour de l'égalité

  1. Qu'affiche le programme suivant et comment expliquer ce résultat?
      Point p1 = new Point();
      Point p2 = new Point();
      System.out.println(p1 == p2);
    L'opérateur == agit en général sur les types primitifs. Lorsqu'il est utilisé sur des références à des objets, il teste l'égalité des références aux objets. En d'autre termes, il teste les valeurs qui ont été retournées par des appels à l'opérateur new. En ce sens, les deux points p1 et p2 qui ont été créés correspondent à deux références différentes, même si elles réfèrent des objets identiques (leur état est identique).
  2. Définir une méthode public boolean same(Point p) qui retourne true si le paramètre p à même x et même y que le point auquel on applique la méthode et false sinon. Ainsi, si on appelle p1.same(p2) dans l'exemple ci-dessus, le résulat doit être true.

Surcharge de méthode

Surchargez, dans la classe Point, la méthode public void translate(double dx, double dy) avec une méthode public void translate(double d) qui déplace à la fois x et y de la valeur d. Testez cette méthode.
Tout comme les constructeurs, les méthodes peuvent être surchargées: cela signifie que plusieurs méthodes ayant le même nom cohabitent dans la même classe. Elle doivent se distinguer par des listes de paramètres différents.
Attention: dans une classe, deux méthodes ne peuvent pas avoir le même nom et les même paramètres, même si elles ont des types de retour différents.

Classe disque

On veut maintenant définir la classe des disques, un disque étant défini par son centre, qui est un Point, et par son rayon, que l'on représentera par un double.
Le fait qu'un disque soit construit à partir d'un point (instance d'une classe existant déjà) s'appelle de la composition.

  1. Définir la classe Disque avec ses champs centre et rayon.
  2. Définir un constructeur acceptant en argument un Point et un double pour initialiser le disque créé.
  3. Définir un constructeur sans argument initialisant le centre du disque au point par défaut (celui construit par le constructeur de point sans argument) et le rayon à 1.
  4. Définir une méthode toString() dans la classe Disque qui donne une représentation sous la forme:
    Disque - centre: (0.0,0.0) rayon: 1.0
    Il est important ici d'utiliser la méthode toString() de la classe Point: c'est de la délégation (dans l'affichage d'un disque, l'affichage de son centre qui est un point est délégué à la classe Point qui a la responsabilité de l'affichage de n'importe laquelle de ses instances.
  5. Dans le même ordre d'idées, définir dans la classe Disque une méthode public void translate(double dx, double dy) qui déplace un disque.
  6. Définir une méthode retournant la surface d'un disque.
  7. Définir une méthode testant l'égalité de deux Disques.

Les exercices à faire pour le 14 décembre 2001 sont décrits ici

À faire

Je rappelle que les exercices donnés à la fin de chaque feuille de séance doivent être effectués soit au cours de la séance, soit en travail personnel, et les fichiers source (.java) correspondant doivent m'être envoyés par mail à l'adresse Etienne.Duris[at]univ-mlv.fr. Ces exercices sont corrigés et constituent des notes de contrôle continu qui comptent dans l'évaluation du module.

  1. Définir les classes Point et Disque décrites ci-dessus avec toutes leurs méthodes.
  2. Définir une méthode isIn dans la classe Disque, qui accepte un Point en argument, qui retourne true si le point est dans le disque et false sinon.
  3. Définir une méthode howManyIn dans la classe Disque, qui accepte un tableau de Points en argument et qui retourne le nombre de Points du tableau qui sont dans le disque auquel la méthode est appliquée.

Etienne.Duris[at]univ-mlv.fr - © École Nationale des Ponts et Chaussées - Décembre 2001 - http://www-igm.univ-mlv.fr/~duris/ENPC/