HÉRITAGE 2018-11-22 IFT1020 - A03
OBJECTIFS Comprendre comment fonctionne l’héritage en Java ainsi que la surdéfinition des méthodes d’une classe parent (superclasse, classe ancêtre). Être capable de faire appel au constructeurs, méthodes et champs des superclasses. Comprendre la superclasse commune Object et surdéfinir ses méthodes toString, equals et clone. 2018-11-22 IFT1020 - A03
Codes Baloune Etang Attachee Coloree Effacable 2018-11-22 IFT1020 - A03
Définition intuitive Par héritage on entend la propriété que possède les instances de sous-classes d’accéder aux données et aux méthodes associées à sa super-classe. 2018-11-22 IFT1020 - A03
Diagramme UML : Baloune Coloree Effacable Attachee Les instances des sous-classes de Baloune bénéficient du travail accompli pour créer (constructeur), gonfler, degongler, deplacer, afficher, etc, des balounes. 2018-11-22 IFT1020 - A03
Une hiérarchie d’organismes vivants Animal Plante Carnivore Herbivore Organisme 2018-11-22 IFT1020 - A03
Hiérarchie d’héritage Les hiérarchies de classes, sous-classes et sous-sous-classes sont communes Exemple: Hiérarchie de classes dans Swing La superclasse JComponent possède les méthodes getWidth, getHeight qui s’appliquent à toutes les sous-classes d’objets de l’interface-usager (UI). La classe AbstractButton possède les méthodes pour régler (set) et obtenir (get) les données des objets boutons, par exemple les textes et icônes associés. 2018-11-22 IFT1020 - A03
Une partie de la hiérarchie des composantes de l’interface-usager (UI) Swing 2018-11-22 IFT1020 - A03
Dualité extension vs. restriction Les données et méthodes associées aux sous-classes sont toujours des extensions des propriétés des classes parents car les sous-classes possèdent toutes les propriétés associées aux super-classes ET d’autres propriétés qui lui sont propres. Une sous-classe possède donc PLUS que les superclasses et, donc, est plus spécialisée que ses superclasses. Conséquemment et dans un certain sens, elle est donc une restriction des superclasses. Ces deux caractéristiques de l’héritage provoque une tension dans notre vision de l’héritage entre le voir comme une extension versus comme une restriction. 2018-11-22 IFT1020 - A03
Transitivité L’héritage est transitif. Une classe hérite de ses Baloune L’héritage est transitif. Une classe hérite de ses superclasses de tous les niveaux. La classe Effacable est une sous-classe de Coloree qui est une sous-classe de Baloune, alors Effacable hérite des attributs de Coloree et de Baloune. Attachee Coloree Effacable 2018-11-22 IFT1020 - A03
Le test est un ? Il existe une règle pour savoir si deux concepts devraient être liés par une relation d’héritage, la règle est-un : si le concept A peut être lié par une relation d’héritage au concept B, la phrase « A est-un(e) B » devrait avoir du sens. Les phrases suivantes sont sensées : Une attachée est une baloune. Un oiseau est un animal. Un chat est un mammifère. Une fenêtre de texte est une fenêtre. Une sphère est un objet graphique. Un tableau d’entiers est un tableau. Par contre, les phrases suivantes sont inappropriées, quelque chose ne sonne pas bien avec elles, et donc l’héritage n’est pas approprié : Un oiseau est un mammifère. Un moteur est une voiture. Une balle est un mur. Un tableau d’entiers est un entier. Il y a des fois ou l’héritage s’applique même si la règle échoue. Néanmoins, pour la majorité des cas, la règle est un bon indicateur du bon emploi de l’héritage. 2018-11-22 IFT1020 - A03
Raisons d’utiliser l’héritage Deux motivations principales : Réutilisation du code. Parce que la sous-classe hérite des méthodes des superclasses, le code n’a pas besoin d’être réécrit, ce qui peut réduire considérablement la quantité de code nécessaire pour développer une nouvelle idée. Réutilisation du concept. Ceci arrive quand une sous-classe surdéfinit les méthodes de ses superclasses. Bien que le code n’est pas réutilisé, les deux classes partagent tout de même les spécifications des méthodes surdéfinies. 2018-11-22 IFT1020 - A03
Baloune… Une Baloune est un objet sphérique d’un certain rayon, positionné en 2D à (x, y). On peut gonfler, degonfler, deplacer et afficher une baloune. On peut aussi vérifier si un point (x, y) est à l’intérieur de la baloune (contient), si une autre baloune est à l’intérieur de la baloune (contient) et si une autre baloune touche à la baloune. (x, y) rayon 2018-11-22 IFT1020 - A03
Allons voir le code de Baloune… 2018-11-22 IFT1020 - A03
contient( int px, int py ) d <= rayon rayon (px, py) d 2018-11-22 IFT1020 - A03
contient( Baloune b ) d + b.rayon <= rayon rayon d b.rayon 2018-11-22 IFT1020 - A03
touche( Baloune b ) d <= rayon + b.rayon rayon d b.rayon 2018-11-22 IFT1020 - A03
Héritage et méthodes Surdéfinir une méthode : fournir une implantation différente d’une méthode qui existe dans la superclasse Hériter d’une méthode : ne pas fournir de nouvelle implantation d’une méthode qui existe dans la superclasse Ajouter une méthode : fournir une nouvelle méthode qui n’existe pas dans la superclasse 2018-11-22 IFT1020 - A03
Héritage et champs Champs hérités : tous les champs de la superclasse sont automatiquement hérités Ajouter champs : fournir un nouveau champs qui n’existe pas dans la superclasse On ne peut pas surdéfinir les champs 2018-11-22 IFT1020 - A03
Coloree Une baloune Coloree est une baloune avec une couleur class Coloree extends Baloune { nouvelles méthodes nouveaux champs } Toutes les méthodes de Baloune sont automatiquement héritées Il est donc correct d’appeler gonfler, degongler, etc sur les objets de la classe Coloree Notez que l’héritage diffère de la réalisation d’une interface Une interface n’est pas une classe Une interface ne fournit pas d’instances de champs ou de méthodes à hériter. (x, y) rayon 2018-11-22 IFT1020 - A03
Répartition des champs d’une sous-classe Coloree couleur; Portion locale x, y, rayon; Portion héritée 2018-11-22 IFT1020 - A03
Allons voir le code de Coloree… 2018-11-22 IFT1020 - A03
Attachee Une baloune Attachee est une baloune avec une corde class Attachee extends Baloune { nouvelles méthodes nouveaux champs } Toutes les méthodes de Baloune sont automatiquement héritées Il est donc correct d’appeler gonfler, degongler, etc sur les objets de la classe Attachee rayon (x, y – longeurCorde - rayon) longueurCorde (x, y) 2018-11-22 IFT1020 - A03
Allons voir le code de Attachee… 2018-11-22 IFT1020 - A03
gonfler 2018-11-22 IFT1020 - A03 rayon (x, y – longeurCorde - rayon) longueurCorde (x, y) 2018-11-22 IFT1020 - A03
Règles de surdéfinition Pour définir une méthode dans une sous-classe de manière différente de la superclasse, on peut la surdéfinir, il suffit de la (re)déclarer dans la sous-classe. On accède à la méthode de la superclasse en utilisant la syntaxe : super.<nom> (d’où le nom superclasse). Souvent, la première étape d’une méthode surdéfinie est de faire appel à la méthode de la superclasse. On peut bloquer le comportement d’une méthode en la redéfinissant mais en la laissant vide (sans code). 2018-11-22 IFT1020 - A03
Redéfinitions dans Attachee On peut la gonfler bien sûr ! Mais il faut tenir compte de la corde qui ne bouge pas lors du gonflage. L’affichage doit aussi tenir compte de la corde. On doit donc redéfinir gonfler et afficher. On veut également empêcher de déplacer une attachée. On doit redéfinir deplacer mais ne pas l’implanter pour simuler le blocage. 2018-11-22 IFT1020 - A03
Champs hérités private Considérez la méthode gonfler de Attachee public boolean gonfler(int r) { // augmenter rayon de la baloune // mettre à jour r2 et diametre } On ne peut pas simplement mettre à jour r2 et diametre car ces champs sont private dans la superclasse La sous-classe doit faire appel à des interfaces public 2018-11-22 IFT1020 - A03
Invoquer une méthode de la superclasse On ne peut pas juste appeler gonfler(r) dans la méthode gonfler de Attachee Ce serait la même chose que this.gonfler(r) qui appellerait la même méthode récursivement (récursivité infinie) Plutôt, il faut invoquer la méthode de la superclasse avec la syntaxe super.gonfler(r) qui appelle la méthode gonfler de la classe Baloune : public boolean gonfler(int r) { if(super.gonfler(r)) { super.deplacer(0, -r); return true; } else return false; } 2018-11-22 IFT1020 - A03
Invoquer le constructeur de la superclasse public class Attachee extends Baloune { public Attachee(int x, int y, int rayon, int longueurCorde) { // constructeur de la superclasse super(x, y - longueurCorde - rayon, rayon); // initialise la longueur de la corde this.longueurCorde = longueurCorde; } ... On passe les arguments au constructeur de la superclasse Doit être le premier énoncé du constructeur de la sous-classe. 2018-11-22 IFT1020 - A03
Règles d’héritage pour les constructeurs Une sous-classe peut déclarer son propre constructeur ou utiliser le constructeur sans argument hérité de la superclasse. Les aspects importants de l’héritage des constructeurs sont : Si la sous-classe possède de nouvelles instances de variables qui ne sont pas dans la superclasse alors le constructeur sans argument hérité initialise ces variables à des valeurs de défaut, comme zéro pour les int, et ensuite exécute les instructions du constructeur de la superclasse. Les autres constructeurs, avec arguments, ne peuvent pas être hérités. Si la sous-classe déclare un constructeur, avec ou sans arguments, alors aucun constructeur de la superclasse n’est hérité, pas même celui sans argument. 2018-11-22 IFT1020 - A03
Allons voir le code de Attachee… 2018-11-22 IFT1020 - A03
Conversion entre références de sous-classes et superclasses Il est correct d’assigner une référence de sous-classe à une référence de superclasse. Attachee ba = new Attachee(30, 40, 10, 20); Baloune b = ba; Object unObjet = ba; Les références de superclasse par contre ne connaissent pas tout sur les sous-classes : b.couperCorde(); // ERROR 2018-11-22 IFT1020 - A03
Des variables de types différents réfèrent à la même instance d’objet Attachee x 30 y 40 rayon 10 longueurCorde 20 ba b unObjet 2018-11-22 IFT1020 - A03
Polymorphisme La méthode générique : public void deplacer(int dx, int dy) { x = x + dx; y = y + dy; } fonctionne avec n’importe quel type de baloune (ordinaire, attachée, colorée, effacable) Les références à des objets de sous-classes sont converties à des références de la superclasse uneAttachee.contient(uneColoree); Le polymorphisme : uneAttachee.gonfler(r) appelle Attachee.gonfler (qui tient compte de la corde) Pourquoi ne pas simplement déclarer les arguments des méthodes comme Object ? La classe Object n’a pas de champs rayon 2018-11-22 IFT1020 - A03
static Le mot réservé « static » permet d’appeler une fonction directement, soit sans invoquer un objet particulier. On peut ainsi utiliser cette méthode directement. On utilise aussi static pour définir des méthodes de comparaison de deux objets sans avoir à passer par un des objets. Par exemple, la méthode touche dans Baloune aurait pu être définie « static », on l’aurait renommer seTouche( Baloune a, Baloune b ). On appelle la méthode directement avec les deux arguments, sans précéder d’un objet. Toute méthode écrite pour Baloune fonctionnera pour n’importe quelle classe dérivée de Baloune. 2018-11-22 IFT1020 - A03
final Si on ne veut pas qu’une méthode puisse être redéfinie, on peut précéder sa définition par le mot réservé « final ». Le mot peut aussi être utilisé avec des variables, les transformants ainsi en constantes et même avec des classes pour empêcher de créer de nouvelles classes dérivées de celle-ci. 2018-11-22 IFT1020 - A03
Liaison dynamique Les méthodes « static », « final » et « private », en fait toutes celles qu’on ne peut pas redéfinir, ne passent pas par le mécanisme de polymorphisme. Il n’y a jamais d’ambiguïté et elles peuvent toujours êtres appelées directement, elles sont candidates pour être définies « inline », c’est-à-dire que le compilateur insert leurs instructions dans le code généré plutôt que d’insérer des appels de méthodes. 2018-11-22 IFT1020 - A03
Surdéfinition des variables Surdéfinir une variable n’est pas possible au sens de l’héritage. On peut cependant déclarer des variables de même nom, appelé l’ombrage des variables. L’ombrage ne passe jamais par le mécanisme de liaison dynamique, càd que les variables accédées sont toujours celles de la classe de la référence (fixée à la définition) et non pas celles des instances référées au moment de l’accès. 2018-11-22 IFT1020 - A03
Classes abstraites Une méthode abstraite est une spécification de méthode sans implantation. En Java, on utilise le mot réservé « abstract ». Similairement, une classe abstraite est une spécification de classe dont les méthodes sont abstraites. Voici les règles : Toute classe qui définit une méthode abstraite est automatiquement abstraite et doit être déclaré ainsi. Une classe abstraite ne peut être instanciée (on ne peut pas créer des instances de ce type). Une sous-classe de classe abstraite peut être instanciée si elle implante toutes les méthodes abstraites de la superclasse. Si une sous-classe d’une classe abstraite n’implante pas toutes les méthodes abstraites qu’elle hérite, cette sous-classe est abstraite. 2018-11-22 IFT1020 - A03
Exemple de classes abstraites… public abstract class A { public abstract double d1(); public abstract double d2(); } public class B extends A { public double d1() { return 0.0; } public double d2() { return 1.0; } public class C extends A { public double d1() { return 100.0; } public double d2() { return 101.0; } 2018-11-22 IFT1020 - A03
…Exemple A[] objs = new A[2]; double blackHole; objs[0] = new B(); objs[1] = new C(); blackHole = objs[0].d1(); // 0.0 blackHole = objs[0].d2(); // 1.0 blackHole = objs[1].d1(); // 100.0 blackHole = objs[1].d2(); // 101.0 Les sous-classes de A peuvent être affectées aux éléments d’un tableau de A. Sans qu’aucun « cast » soit nécessaire. La liaison dynamique permet d’appeler les méthodes de A même si la classe A n’a pas implanté ces méthodes. Si les méthodes n’avaient pas été déclarées dans A, une erreur aurait été signalée à la compilation. 2018-11-22 IFT1020 - A03
Niveaux d’accès de contrôle public private protected (accessible par les sous-classes dans le package) Accès package (par défaut) 2018-11-22 IFT1020 - A03
Mécanismes d’accès… Visibilité public défaut protected private protected private Aux non-sous-classes du même package oui non Aux sous-classes du même package Aux non-sous-classes d’un autre package Aux sous-classes d’un autre package Hérité par les sous-classes du même package Hérité par les sous-classes d’un autre package 2018-11-22 IFT1020 - A03
Mécanismes d’accès… 2018-11-22 IFT1020 - A03
…Mécanismes d’accès 1. Si une variable ou une méthode (un membre) est « public » dans une classe, elle l’est aussi dans toutes ses sous-classes. Un membre « public » est accessible aux clients de la classe et des sous-classes (puisque le membre est « public » dans toutes ses sous-classes). 2. Si un membre est « private » dans une classe, son utilisation est restreinte à cette classe et il n’est pas accessible ni dans les sous-classes ni pour les clients de la classe ou des sous-classes. 3. Si un membre est « protected » dans une classe, il est accessible dans toutes les sous-classes mais il ne l’est pas pour les clients de la classe ou des sous-classes. 2018-11-22 IFT1020 - A03
Niveaux d’accès recommandés Champs: toujours private Exceptions pour les constantes : public static final Méthodes: public ou private Classes: public ou protected Mais en fait, il n’est pas recommandé d’utiliser protected Et attention aux accès par défaut 2018-11-22 IFT1020 - A03
Object : la superclasse “cosmique” Toutes les classes étendent Object Méthodes les plus utiles : String toString() boolean equals(Object unAutreObject) Object clone() 2018-11-22 IFT1020 - A03
La classe Object est la superclasse de toute classe Java Baloune Attachee Coloree 2018-11-22 IFT1020 - A03
Surdéfinir la méthode toString Retourne une représentation String de l’objet (utile pour “debugger”) Exemple: Rectangle.toString retourne quelque chose comme java.awt.Rectangle[x=5,y=10,width=20,height=30] toString est utilisé par l’opérateur de concatenation uneString + unObjet (veut dire) uneString + unObjet.toString() Objet.toString imprime le nom de sa classe et l’adresse de l’objet Baloune@d2460bf Surdéfinir toString : public class Baloune { public String toString() { return "Baloune(" + x + ", " + y + ", " + rayon + ", " + r2 + ")"; } . . . } 2018-11-22 IFT1020 - A03
Surdéfinir la méthode equals equals test pour une égalité de contenu == test pour une égalité de références On doit caster l’objet passé en argument public class PieceMonnaie { public boolean equals(Object autreObjet) { PieceMonnaie autre = (PieceMonnaie)autreObjet; return nom().equals(autre.nom()) && valeur == autre.valeur; } … } 2018-11-22 IFT1020 - A03
b1 equals b2 b1 b2 Attachee Attachee x 30 y 40 rayon 10 longueurCorde 20 b1 Attachee x 30 y 40 rayon 10 longueurCorde 20 b2 2018-11-22 IFT1020 - A03
ba == b ba b Attachee x 30 y 40 rayon 10 longueurCorde 20 2018-11-22 IFT1020 - A03
Surdéfinir la méthode clone Copier la référence d’un objet donne deux références au même objet Balooune b2 = b1; Parfois, on doit faire une copie d’un objet Utilisez alors la méthode clone: Baloune b2 = (Baloune)b1.clone(); On doit caster la valeur retournée car le type retourné par clone est Object Définir la méthode clone pour faire de nouveaux objets: public Object clone() { Baloune clonee = new Baloune(); … return clonee; } ATTENTION : Cette approche ne marche pas avec l’héritage. 2018-11-22 IFT1020 - A03
À compléter… 2018-11-22 IFT1020 - A03
Pétage de balounes On veut développer un jeu de pétage de balounes. Des balounes de tailles et couleurs différentes s’affichent à l’écran et à l’aide de la souris on les pète en cliquant dessus. Le jeu calcule le temps pris pour péter toutes les balounes et nous classe dans trois catégories selon le temps pris : « très rapide », « assez rapide » et « pas vite ». Le jeu permet aussi de choisir une vitesse d’affichage des balounes: « lent », « moyen » ou « rapide ». 2018-11-22 IFT1020 - A03