Programmation 2 : cinquième cours

Arnaud Labourel

15 ou 17 octobre 2018

Classes abstraites et extension

Classe ListSum

Supposons que nous ayons la classe suivante :

Classe ListProduct

Supposons que nous ayons aussi la classe suivante (très similaire) :

Refactoring pour isoler la répétition

Les deux classes sont très similaires et il y a clairement de la répétition de code.

Il est possible de refactorer (réécrire le code) les classes précédentes de façon à isoler les différences dans des méthodes :

Refactoring pour isoler la répétition

Il est possible de refactorer les classes précédentes de façon à isoler les différences dans des méthodes :

Comment éviter la répétition ?

Après la refactorisation du code :

  • seules les méthodes neutral et compute diffèrent
  • il serait intéressant de pouvoir supprimer les duplications de code

Deux solutions :

  • La délégation en utilisant une interface
  • L’extension et les classes abstraites

Interface Operator

Nous allons externaliser les méthodes neutral et compute dans deux nouvelles classes. Elles vont implémenter l’interface Operator :

Délégation

Les classes ListSum et ListProduct sont fusionnées dans une unique classe List qui délègue les calculs à un objet qui implémente l’interface Operator :

Délégation

Utilisation des classes ListSum et ListProduct :

Utilisation après la refactorisation du code :

Classes abstraites

Mot-clé abstract

Classe abstraite

  • On peut mettre abstract devant le nom de la classe à sa définition pour signifier qu’une classe est abstraite.
  • Une classe est abstraite si des méthodes ne sont pas implémentées.

    \(\Rightarrow\) Classe abstraite = classe avec des méthodes abstraites

  • Tout comme pour un interface, une classe abstraite n’est pas instanciable.

Méthode abstraite

  • abstract devant le nom de le type de retour de la méthode à sa définition pour signifier qu’une méthode est abstraite.
  • Méthode abstraite = méthode sans code, juste la signature (type du retour et des paramètres) est définie

Classes abstraites et extension

Tout comme pour les interfaces, il n’est pas possible d’instancier une classe abstraite :

Nous allons étendre la classe List afin de récupérer les propriétés et les méthodes de List et définir le code des méthodes abstraites :

Classes abstraites et extension

La classe ListSum n’est plus abstraite, toutes ses méthodes sont définies :

  • les méthodes add et eval sont définies dans List et ListSum hérite du code de ses méthodes.
  • les méthodes neutral et compute qui étaient abstraites dans List sont définies dans ListSum.

On peut donc instancier la classe ListSum :

Classes abstraites et extension

On peut procéder de manière similaire pour créer une classe ListProduct

La classe ListProduct n’est plus abstraite, toutes ses méthodes sont définies :

Généralisation de l’extension aux classes non-abstraites

Plus généralement, l’extension permet de créer une classe en :

  • conservant les services (propriétés et méthodes) d’une autre classe;
  • ajoutant de nouveaux services (propriétés et méthodes);
  • redéfinissant certains services (méthodes).

En Java :

  • On utilise le mot-clé extends pour étendre une classe ;
  • Une classe ne peut étendre qu’une seule classe.

Important

Il est toujours préférable de privilégier l’implémentation à l’extension.

Extension pour ajouter des nouveaux services

Supposons que nous ayons la classe Point suivante :

Il est possible d’ajouter de nouveaux services en utilisant l’extension :

Extension pour ajouter des nouveaux services

Les services (méthodes et attributs) de Point sont disponibles dans Pixel :

Évidemment, les services de Pixel ne sont pas disponibles dans Point :

Redéfinition de méthode

Supposons que nous ayons la classe Point suivante :

Redéfinition de méthode

Il est possible de redéfinir la méthode clear dans Point :

Redéfinition avec super

Le mot-clé super permet d’utiliser la méthode clear de Point :

Le mot-clé super

Si la méthode n’a pas été redéfinie, le mot clé super est inutile :

Les constructeurs et le mot-clé super

La classe Point n’a pas de constructeur sans paramètre, il faut donc indiquer comment initialiser la partie de Pixel issue de la classe Point :

Les constructeurs

Supposons que nous ayons la classe Point suivante :

Par défaut, le constructeur sans paramètre est appelé :

Les constructeurs

Ici, vous devez préciser les paramètres du constructeur avec super :

Transtypages et polymorphisme

Aucune méthode ou propriété ne peut être supprimée lors d’une extension. Par exemple, Pixel possède toutes les propriétés et méthodes de Point (même si certaines méthodes ont pu être redéfinies).

Par conséquent, l’upcasting est toujours autorisé :

Remarques

  • Le code exécuté lors d’un appel de méthode est déterminé à l’exécution en fonction de la référence présente dans la variable.
  • Le typage des variables permet de vérifier à la compilation l’existence des propriétés et des méthodes.

Upcasting

Définition d’upcasting

Considérer une instance d’une classe comme une instance d’une de ses super-classes, c’est-à-dire :

  • une classe étendue par la classe
  • une interface implémentée par la classe

Remarques

  • Après l’upcasting, les services supplémentaires de la classe (méthodes et attributs non-disponibles dans la super-classe) ne sont plus accessibles.
  • Lors de l’appel des méthodes, si la classe a redéfini (overrides) la méthode, c’est le code de la classe qui est exécuté.

La classe Object

Par défaut, les classes étendent la classe Object de Java. Par conséquent, l’upcasting vers la classe Object est toujours possible :

Notez que object.setPosition(2,3) n’est pas autorisé dans le code ci-dessus car la classe Object ne possède pas la méthode setPosition et seul le type de la variable compte pour déterminer si l’appel d’une méthode ou l’utilisation d’une propriété est autorisé.

Méthodes de la classe Object

  • protected Object clone(): Creates and returns a copy of this object.
  • boolean equals(Object obj): Indicates whether some other object is “equal to” this one.
  • Class<?> getClass(): Returns the runtime class of this Object.
  • int hashCode(): Returns a hash code value for the object.
  • String toString(): Returns a string representation of the object.

Il y a aussi des méthodes wait et notify pour attendre sur un objet et réveiller des thread en attentes sur un objet : cours de Programmation objet concurrente en Master 1 informatique d’AMU.

La méthode toString() de la classe Object

Par transitivité de l’extension, toutes les méthodes et propriétés de la classe Object sont disponibles sur toutes les instances :

La méthode toString est utilisée par Java pour convertir une référence en chaîne de caractères :

La méthode toString() et le polymorphisme (1/2)

Évidemment, il est possible de redéfinir la méthode toString :

La méthode toString() et le polymorphisme (2/2)

Le polymorphisme fait que cette méthode est appelée si la variable contient une référence vers un Point :

Rappel : extension d’interface

Supposons que nous ayons l’interface suivante :

En Java, on peut aussi étendre une interface :

Une classe qui implémente l’interface ModifiableList doit implémenter les méthodes size, get, add et remove.

Extension d’interface

Supposons que la classe ArrayModifiableList implémente l’interface ModifiableList. Dans ce cas, nous pouvons écrire :

En revanche, il n’est pas possible d’écrire :

Extension de plusieurs interfaces

Supposons que nous avons l’interface suivante :

En Java, une interface peut étendre plusieurs interfaces :

Remarques

  • Nous ne définissons pas de nouvelles méthodes dans l’interface ModifiablePrintableList.

    \(\Rightarrow\) Cette interface ne représente que l’union des interfaces ModifiableList et Printable.
  • De nouvelles méthodes auraient pu être définies dans ModifiablePrintableList.

Extension et accessibilité

Accessibilité : modificateur public et default

Une classe ou un membre (attribut ou méthode) est accessible :

  • de n’importe où s’il est précédé du modificateur public ;
  • des classes de son paquet si rien n’est précisé (default).

Remarques :

  • Seules les classes publiques sont utilisable à partir d’un autre paquet
  • Un fichier ne peut contenir qu’une seule classe publique

Accessibilité : modificateur private

Un membre (attribut ou méthode) est accessible :

  • de n’importe où s’il est précédé du modificateur public
  • des classes de son paquet si rien n’est précisé (default)
  • des méthodes de la classe s’il est précédé de private

Afin de limiter les conséquences d’une modification :

  • Les méthodes ou propriétés définies pour rendre lisible l’implémentation des fonctionnalités doivent être privées ;
  • Seule l’interface de la classe doit être publique.

Accessibilité : modificateur protected

Un membre (attribut ou méthode) protected n’est accessible que par les méthodes des classes du paquet et par les classes qui l’étendent.

Le modificateur protected permet de protéger un membre :

Modificateur Classe Paquet Extension Extérieur
private \(\checkmark\)
default \(\checkmark\) \(\checkmark\)
protected \(\checkmark\) \(\checkmark\) \(\checkmark\)
public \(\checkmark\) \(\checkmark\) \(\checkmark\) \(\checkmark\)