Travaux Pratiques n°3

Objectifs (1h30)

  • Savoir implanter la notion d'interface en Java
  • Comprendre la différence entre héritage et délégation
  • Savoir utiliser les classes paramétriques

Rendu du travail

Le TP doit être rendu en fin séance, quel que soit son état d'avancement. Envoyez le source des classes (.java) ainsi que les classes compilées (.class) dans une archive zip (voir les instructions du TP1b et veiller à bien ajouter les sources !) à Gauthier Picard la fin, avec comme sujet [LCPOO] TP3 <nom1> <nom2>. L'absence d'envoi sera sanctionnée dans la note finale.

Classes abstraites et interfaces : exemple des collections

Nous avons vu en TD les notions de classes abstraites et d'interfaces. En UML les classes et méthodes abstraites sont indiquées avec le mot {abstract} ou leur nom écrit en italique. Les interfaces sont indiquées par {interface} ou <<interface>>.

Ces notions sont très usitées dans les librairies fournies avec Java. Notamment, les collections, que vous avez déjà utilisées en sont un bon exemple. Le schéma suivant présente une partie des héritages et des implémentations à partir de la classe concrète ArrayList<E> (notons qu'un type passé en paramètre d'une classe est noté par un rectangle en haut à droite de la classe).

list.png

En partant de ArrayList, nous pouvons constater :

  • ArrayList hérite de la classe abstraite AbstractList et implémente donc les méthodes get et size qui sont abstraites dans AbstractList.
  • AbstractList hérite de la classe abstraite AbstractCollection ce qui lui fournit notamment les méthodes add, remove et size provenant de l'interface Collection (voir ci-dessous), et implémente également l'interface List qui lui fournit surtout les méthodes get et set pour accéder en lecture et en écriture à des éléments particulier de la liste.
  • AbstractCollection hérite directement de la classe Object et implémente l'interface Collection nécessitant les méthodes add, remove et size.
  • L'interface List hérite de l'interface Collection, et lui ajoute les méthodes get et set.

Donc par polymorphisme, une ArrayList peut également être utilisée comme une AbstractList, comme une AbstractCollection, comme un Object, comme une List et comme une Collection.

Liste triée par héritage

Généralisation de la liste triée de String

Au TP précédent, vous avez défini une liste triée de chaînes de caractères : SortedListOfStrings. Par héritage vous avez pu définir qu'une liste triée est une ArrayList<String> dont la méthode add est modifiée de telle sorte que la chaîne à insérer soit à la bonne place dans la liste au lieu d'être ajoutée en fin de liste. Sans le savoir vous avez utilisé l'interface @<tt>Comparable@</tt> qu'implémente la classe String de Java, en utilisant la méthode compareTo. Donc, votre méthode pour insérer une chaîne de caractère peut être utilisée pour n'importe quelle classe implémentant l'interface Comparable (à quelques modifications de déclarations près).

Exercice 1

En vous inspirant de la correction de la classe SortedListOfStrings, écrivez une classe SortedList permettant de ranger n'importe quel objet à partir du moment où cet objet est comparable aux objets contenus dans la liste (et inversement). En d'autres termes, ceci signifie qu'au lieu de ranger des objets de type String votre classe doit ranger des objets de type quelconque E qui soit comparable à des objets de type E. En Java, on note ce type E extends Comparable<E> comme présenté dans le code et la figure suivants :

public class SortedList<E extends Comparable<E>> extends ArrayList<E> {
   ...
}

sortedlist.png

Ainsi, si vous pourrez utiliser des listes contenant des types différents comme dans l'exemple suivant :

SortedList<String> sls = new SortedList<String>();
sls.add("Hello");
sls.add("World");
SortedList<Integer> sli = new SortedList<Integer>();
sli.add(1);
sli.add(2);

Classe de test

Pensez à tester votre code dans une classe à part contenant une méthode main comme dans le TP précédent. Vous pouvez vous inspirer de la classe SortedListOfStringsTest donnée en solution du TP2.

Liste triée par délégation

Exercice 2

Dans un package de votre projet Java, écrivez la classe SortedListUsingDelegation utilisant la délégation au lieu de l'héritage, comme le montre la figure suivante, en vous inspirant de la solution de la classe SortedListOfStringsUsingDelegation.

sortedlist2.png

Pour rappel, la délégation signifie simplement qu'au lieu d'hériter de la classe ArrayList, la classe SortedListUsingDelegation encapsule un objet (donc possède un attribut) du type ArrayList et le manipule pour exhiber le comportement d'une liste. Afin d'avoir des accès à cette liste, il faut par contre définir des méthodes.

Exercice 3

Ecrivez et testez donc les méthodes présentes dans la figure :

  • add pour ajouter un élément à la bonne place,
  • get pour récupérer le ième élément,
  • peek pour récupérer le premier élement de la liste,
  • clear pour vider la liste,
  • remove pour supprimer un élément de la liste,
  • size pour récupérer la taille de la liste,
  • isEmpty pour déterminer si la liste est vide,
  • contains pour déterminer si un élément appartient à la liste.

Par contre, comme cela a été souligné au TP précédent, notre liste conçue par délégation n'est plus utilisable en tant que liste (pas de polymorphisme). Du coup, il n'est pas possible par exemple de l'utiliser dans un contexte de liste itérable comme le suivant :

SortedListUsingDelegation<Integer> sliud = new SortedListUsingDelegation<Integer>();
sliud.add(1);
sliud.add(2);
for (Integer i : sliud) //erreur à la compilation !!!
   System.out.println(i);

Pour résoudre ce problème, nous pouvons proposer par exemple de faire de notre SortedListUsingDelegation une concrétisation de l'interface Iterable, et donc utilisable dans de tels contextes. Pour être encore plus complets, nous pourrions également implémenter l'interface List mais ceci nécessiterait de coder les 25 méthodes de cette interface.

Exercice 4

Modifiez la classe SortedListUsingDelegation afin qu'elle implémente l'interface Iterable. Ceci signifie qu'il faut que votre classe présente la méthode iterator() qui doit renvoyer l'itérateur de la liste encapsulée par la classe.


Gauthier Picard