Programmation Objet en JAVA
Cours 2 : Réutilisation, Héritage 1
Agrégation, composition, héritage, Hiérarchie, Constructeurs, Transtypage
Réutilisation ?
• Comment utiliser une classe comme matériau pour concevoir une autre classe répondant à de nouveaux besoins ?
• Quels sont les attentes de cette nouvelle classe ? – besoin des «services» d'une classe existante (les données
structurées, les méthodes, les 2). – faire évoluer une classe existante (spécialiser, ajouter des
fonctionnalités, ... )• Quelle relation existe entre les 2 classes ? • Dans une conception objet, des relations sont définies, des règles
d'association et de relation existent.
• Un objet fait appel un autre objet.• Un objet est crée à partir d'un autre : il hérite.
La relation client/serveur - Agrégation
• Un objet o1 de la classe C1 utilise un objet o2 de la classe C2 via son interface (attributs, méthodes).
• o1 délègue une partie de son activité et de son information à o2. • o2 a été construit et existe par ailleurs.• Faire une référence dans C1 vers un objet de C2; • Attention o2 est autonome, indépendant de o1, il peut être partagé. • C'est une agrégation.
public class C1 { private C2 o2; ...}
public class C2 { ... ...}
Le client Le serveur
La relation client/serveur - Agrégation
public class Cercle { private Point
centre; ...}
public class Point { ... ...}
Le client Le serveur
La relation client/serveur - Composition
• Mais si o2 évolue, est-ce grave?
• Comment faire pour que o1 possède son o2 à lui. • La solution se trouve dans le constructeur de C1 : doter o1 d'un objet
o2 qui lui appartienne.• C'est une composition.
public class C1 { private C2 o2; ... C1(...){
o2 = new C2(...); } ...}
public class C2 { ... ...}
Le client Le serveur
La relation client/serveur - Composition
public class Cercle { private Point
centre; ... public Cercle(...){
centre = new Point(...);
} ...}
public class Point { ... ...}
Le client Le serveur
L'exemple du cercle
• Le cercle c1 a un centre. C'est le point p1. • Utilisation d'un objet de type Point, car la classe existe déjà. • Si agrégation, tout changement effectué sur p1 a des conséquences
sur c1. – Si on déplace p1, on déplace tous les cercle ayant comme centre
p1. Composition ? • Mais plusieurs cercle peuvent partager le même centre ... Impossible
si composition. – ça se discute !
• Question : A-t-on besoin du code source de la classe Point pour coder la classe Cercle ?
• Besoin d’un objet existant ? « Se connecter à un serveur » • Confiance en l’encapsulation Agrégation
• Composition et getter.
Nouveau problème
• Si le serveur est insuffisant, incomplet, inadapté ?
– un objet Point répond à un besoin «mathématique».
– Si on veut en faire un objet graphique, avec une couleur et qu'il soit capable de se dessiner ?
– Mais en gardant les propriétés et les fonctionnalités d'un Point.• La solution en POO : l'héritage.
– Définir une nouvelle classe à partir de la définition d'une classe existante.
– Spécialiser, augmenter.
Héritage
public class LaClasseFille extends LaClasseMere{...}
class PointAvecUneCouleur extends Point
• En java, on n'hérite que d'une seule classe.
• Un objet de ClasseFille peut faire tout ce que sait faire un objet de ClasseMere
• Il le fait différemment et/ou il peut plus.
Exemple en Javadoc
Hiérarchie en Héritage
• L'héritage est une notion transitive
• Hiérarchie d'héritage : l'ensemble des classes dérivées d'un parent commun.
• Attention ce n'est pas bijectif :
– un Point3D est un Point.
– Le contraire est faux.
Chaîne d'héritage : le chemin menant d'une classe vers ses ancêtres.
Concept d’héritage
• Concept fondamental des langages objet. • Dériver une nouvelle classe à partir d'une classe existante en
récupérant ses propriétés.• Avantage
– Réutilisation : factorisation, enrichissement de champs et méthodes de classes existantes.
– Spécialisation d’une classe existante sans la modifier.– Pas de limitation en profondeur.
• Contrainte– Héritage simple : une classe ne peut hériter que d'une
seule classe.– Toutes les méthodes et champs ne sont plus regroupées
dans le même fichier
Exemple d'héritage
import Point;public class PointAvecUneCouleur extends Point{ int couleur, x, y; // x et y sont hérités public PointAvecUneCouleur(int couleur) // un
constructeur { super(); // appel au constructeur de la classe mère setCouleur(couleur); } public PointAvecUneCouleur(int x, int y, int couleur) // un
constructeur {
super(x,y); // appel au constructeur de la classe mèresetCouleur(couleur);
} public void setCouleur(int couleur) { this.couleur=couleur; // désigne l'objet encours }
La classe "Object"
• Toutes les classes Java sont des descendants de la classe Object.• Object est parent par défaut des classes qui ne précisent pas leur ancêtres • Object offre quelques services génériques dont la pluspart sont à refaire, ...
à redéfinir (attention au fonctionnement par défaut) :
public final Class getClass(); • Renvoie un objet de type Class qui permet de connaître le nom d'une
classepublic boolean equals(Object o);
• Compare les champs un par un (pas les pointeurs mémoire comme ==)protected Object clone();
• Allocation de mémoire pour une nouvelle instance d'un objet et recopie de son contenu
public String toString(); • Renvoie une chaîne décrivant la valeur de l'objet
La classe "Class"
• C'est la classe qui identifie les types à l'exécution• Permet la sélection des méthodes en fonction de la classe
lors des appels
public String getName(); • Renvoie le nom d'une classe
public static Class forName(String s); • La fonction réciproque de getName
public Object newInstance(); • Crée une nouvelle instance de même type
public String toString(); • Renvoie une chaîne décrivant la valeur de l'objet
La méthode equals()
• Egalité entre objet et non entre référence
• Très utile pour les String– If (myString == "Hello") {…} Toujours Faux !!!– If (myString.equals("Hello") ) {…} OK
• Si les 2 objets o1 et o2 ont des références r1 et r2 comme champs, il faut que r1==r2 pour que o1.equals(o2) et non pas que r1.equals(r2)
• Réflexive, symétrique, transitive et consistent
• Pour les classes que vous créez, vous êtes responsable.
Point p1 = new Point(2,1);Point p2 = new Point(2,1);if (p1==p2){...} // Ici c'est fauxif (p1.equals(p2)){...}// OK si equals est bien redéfinitp1 = p2 // Co-référence if (p1==p2){...} // Vrai désormais
Les fonctions hashCode() et toString()
• hashCode() renvois un chiffre différent pour chaque instance différente• Si o1.equals(o2) alors o1.hashCode()==o2.hashCode()• Le contraire n’est pas assuré mais préférable (@mémoire)• Très utile pour les HashTable.
• toString() conversion en String d'un objet :
String s = '' mon objet visible '' + monObjet;
String s = '' mon objet visible '' + monObjet.toString();
Constructeurs et héritage
● L'héritage permet la réutilisation de code. Le constructeur est concerné. ● Possibilité d'invoquer une méthode héritée : la référence super.● Invocation d'un constructeur de la super-classe :
● super(<params du constructeur>)● L'utilisation de super ressemble à celle de this.● L'appel à un constructeur de la super classe doit toujours être la
première instruction dans le corps du constructeur.● Si ce n'est pas fait, il y un appel implicite : super(). ● Lors de la création d'un objet, les constructeurs sont invoqués en
remontant de classe en classe la hiérarchie, jusqu'à la classe Object. ● Attention, l'exécution se fait alors en redescendant dans la hiérarchie.
● Un constructeur d'une classe est toujours appelé lorsqu'une instance de l'une de ses sous classes est créée.
Héritage et constructeurs - suite
public class Point extends Object{ public Point(double i, double j) {...}}
public class PointAvecUneCouleur extends Point{ public PointAvecUneCouleur(int i, int j, int c){ super(i,j);
couleur=c; }
public class Object{ public Object() {...}}
Retour sur protected
• Dans la classe Point pour setX on a 3 possibilité :
• public : un objet PointAvecUneCouleur peut fixer l'abscisse, ... mais aussi tout le monde.
• private : plus personne ne peut, y compris un objet PointAvecUneCouleur.
• protected : la solution ici, les objets d'une classe héritière ont accès aux membres protected (+friendly).
public void setX (double nouveauX){...} private void setX (double nouveauX){...} protected void setX (double nouveauX){...}
Transtypage et héritage : surclassement.
• Le transtypage consiste à convertir un objet d'une classe en objet d'une autre classe
• Vers une «super-class» (upcast ou surclassement), c'est toujours possible : on peut toujours transtyper vers une super-classe, plus pauvre en informations.
Point3D unPoint3D = new Point3D( x, y, z );Point unPoint = (Point) unPoint3D; // une projection sur xOy
• La mention du transtypage est facultative, mais recommandée pour la lisibilité.
• « un Point3D est un Point mais pas le contraire. »• Un message envoyable à un objet, l'est aussi pour tous les objets d'une
classe héritière :
PointAvecUneCouleur p = new PointAvecUneCouleur();
p.translater(1,3); // Méthode définit au niveau de Point
Transtypage et héritage : surclassement.
• Le transtypage consiste à convertir un objet d'une classe en objet d'une autre classe
• Vers une «super-class» (upcast ou surclassement), c'est toujours possible : on peut toujours transtyper vers une super-classe, plus pauvre en informations.
Point3D unPoint3D = new Point3D( x, y, z );Point unPoint = (Point) unPoint3D; // une projection sur xOy
• La mention du transtypage est facultative, mais recommandée pour la lisibilité.
• Attention : unPoint3D est de type Point3D mais aussi Point. • « un Point3D est un Point mais pas le contraire. »• Un message envoyable à un objet, l'est aussi pour tous les objets d'une
classe héritière :
PointAvecUneCouleur p = new PointAvecUneCouleur();
p.translater(1,3); // Méthode définit au niveau de Point
Transtypage et héritage : sous-classement
• Le comportement d'un objet «hérité» est augmenté par rapport à un objet d'une classe mère.
• Le transtypage vers une sous-classe (downcast ou sous-classement)– Impossible (sauf si un surclassement avait été fait)– pour transtyper vers une sous-classe, utiliser le mot-clé réservé
instanceof.unPoint = new Point3D(...);....if( unPoint instanceof Point3D) { unPoint3D = (Point3D) unPoint; unPoint3D.projection3D(...);}
• La mention du transtypage est obligatoire, dans ce cas.
Un exemple : la banquise
• Un plateau composé de 8x8 cases
– Les 4 coins : terre ferme
– les autres cases : banquise. Lorsqu’un joueur fait fondre une case banquise, les propriétés se révèlent :
• certaines cases ne fondent pas et fait apparaître un renard
• d’autres fondent font apparaître de l’eau et un banc de poissons ou ours
• d’autres donnent au joueur des capacités (elles sont donc garder par le joueur)
• d’autres implique une action immédiate (dérive)
• Pour résumer : deux grande familles de Cases, action et modification du joueur.
• Sur une case, peuvent être posés des pions et des igloos.
banquise : questions
• Comment encoder la notion de plateau ? • Comment encoder la notion de pion, igloo et des différentes actions
possibles ?• Pour les cases :
– Un type d’objet (sa classe) se définit par sa structure de données et son comportement.
– Si deux objets ont des comportements différents, ils appartiennent à deux types différents -> des classes différentes.
– Ce qui est générique à tous : peut contenir des pions et igloos
– Il y a plusieurs types de banquises, toutes ont une action lors de la fonte.
banquise : conception
Case // Contient Pions et Igloos sous forme de vecteurs
| // -> agrégation ou composition ?
|
|--- BanquiseFerme // la tuile change de forme, mais il ne
| // se passe rien.
|--- BanquiseAction // la fonte déclenche une action
| // du joueur -> lui envoie un
| // message spécifique
|--- BanquiseCapacité // la fonte modifie le joueur // -> lui envoie un message
spécifique
• Toutes les banquises sont des cases.• Toutes les cases banquises ont une méthode action.• Cette méthode est différente selon le type de la case (sa classe). • Le plateau est un tableau de Cases
Référence sur une Case
Vector<Pion> pions;
Igloo igloo;
...
Vector<pion> getPions();
boolean addPion();
boolean addIgloo();
boolean equals(Object o);
String toString();
Objet Case en mémoire@0ffa88
Case macase = new Case ( ... );
macase
Référence sur une BanquiseFerme
Vector<Pion> pions;
Igloo igloo; ...
Vector<pion> getPions();
boolean addPion();
boolean addIgloo();
boolean equals(Object o);
String toString();
Objet BanquiseFerme en mémoire
@0ffa98
BanquiseFerme macase =
new BanquiseFerme(...);
macase
public BanquiseFerme extends Case
void action( ... );
Surclassement d’une BanquiseFerme
@0ffa98
Case macase = new BanquiseFerme(null,1);
macase
Code hérité de la classe Case
public BanquiseFerme extends Case
Vector<Pion> pions;
Igloo igloo;
Vector<pion> getPions();
boolean addPion();
boolean addIgloo();
boolean equals(Object o);
String toString();
Objet BanquiseFerme en mémoire
void action( ... );
Surcharge et redéfinition (overload vs override)
public class Point { ... public void maMethode(int i){...}}
public class PointCouleur { ... public void maMethode(int i){...}}
public class Point3D { ... public void maMethode(double i){...}}
RedéfinitionSurcharge
Pour un objet Point3D :• 2 méthodes maMethode
Pour un objet PointCouleur :• une seule maMethode
Redéfinition et surclassement
Vector<pion> getPions();
boolean addPion();
boolean addIgloo();
Objet BanquiseFerme en mémoire
@0ffa98
Case macase = new BanquiseFerme(...);
System.out.println(macase);
macase
public Case {...public String toString(){
return « TerreFerme »;}
}public BanquiseFerme extends Case{
...public String toString(){
return « BanquiseFerme »;}
}
void action();
String toString();
Code hérité de la classe Case
Vector<Pion> pions;
Igloo igloo;
Exemple animalier
Animal
Cheval OursTortue
mangerdormirmarchercourir (2 x marcher)espèce
galopercourir hiberner
Cheval de courseCheval de traie
galopergaloper
Blocage de l'héritage
• Mot-clé final– Le mot-clé final devant une classe indique qu'elle ne peut avoir de
descendance.final class Point3D { ...}• Avantage
– Efficacité : implémentation statique possible.– Sécurité : pas de redéfinition possible des méthodes pour une
descendance.• Inconvénients
– Impossibilité de sous-classer pour créer une classe de test indépendante
Conseils de conception
• Faire des schémas
– Représentant les classes et leur hierarchie• Pour comprendre les interactions entre les classes (qui
a besoin d’une référence sur qui)• Pour savoir ou placer une fonction• Pour bien penser les droits d'accès
– Représentant les appels de fonction• Pour savoir quels objets sont traversés• Pour savoir où récupérer les données à mettre en
argument
• Attention l'héritage n'est pas une réponse à tout, la délégation est aussi utile.
– l'héritage est une relation is-like.