classe Object
Transcription
classe Object
Héritage et polymorphisme Observez les schémas de quelques classes vues. Ils sont utiles pour comprendre les notions d’héritage et de polymorphisme. classe Object public String toString(); public boolean equals(Object autre); . . . etc . . . classe Vector<E> (dérivée de Object) public String toString(); . . . etc . . . Number classe Stack<E> (Pile, dérivée de Vector) public Stack(); public E peek(); public E pop(); public E push(E obj); public boolean empty(); pas de toString() ni de equals() etc . . . classe Integer (déjà vue) public Integer(int n); public String toString(); public boolean equals(Object obj); . . . etc . . . classe StringTokenizer public StringTokennizer(String ch); pas de toString() ni de equals . . . etc . . . Object est la super-classe de toutes les classes. IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 112 La programmation orientée objet est basée sur trois nouveaux concepts : - Encapsulation (déjà vu et travaillé dans les travaux pratiques) - Héritage ( tel père => tel fils et plus!!! ) - Polymorphisme (capacité de se présenter sous plusieurs formes différentes : la lampe magique d’Aladin???) Héritage La POO favorise la réutilisation des classes, des méthodes, du codage. Grâce à l'héritage, on peut modifier une classe sans avoir à la réécrire complètement : Pour gérer une pile selon le principe LIFO (Last In First Out : dernière entrée, première sortie), on n'a pas besoin de casser la tête : la plupart des méthodes existantes dans Vector nous permettent de traiter une pile. Stack est une classe où on profite des méthodes existantes de la classe Vector. Dans les documents de Java, on trouve la description suivante de cette classe : public class java.util.Stack<E> extends java.util.Vector<E> { // constructeur : public Stack(); // construire une pile VIDE // méthodes : public boolean empty() ; // la pile est-elle vide ? // consulter l'objet au sommet sans le retirer de la pile public E peek(); // retirer l'objet au sommet et le retourne public E pop(); // empiler un objet au sommet de la pile public E push ( E item) ; // retourne la distance d'un objet vs le sommet public int search(Object obj); } Le mot extends signale que Stack étend (dérive de) Vector . C'est une sous-classe de Vector. IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 113 Terminologie : Vector est une super-classe de Stack Vector est une classe de base Vector est une surclasse Vector est une classe parente etc …. Stack est une sous-classe de Vector Stack est une classe dérivée de Vector Stack est une classe étendue de Vector Stack est une classe enfante de Vector etc … Comment écrire la méthode push (empiler) de la sous-classe Stack ? public Object push( Object item) { // ajouter l'item à la pile (dans le vecteur!!!) addElement (item ); // retourner l'item (selon la syntaxe de cette méthode ) return item; } Conclusion : on profite (réutilise) la méthode addElement de la classe Vector. Une version plus logique : public void push( E item) { // ajouter l'item à la pile (dans le vecteur!!!) addElement (E ); } Comment afficher le contenu d'une pile ??? Notez que la méthode toString() n'existe pas dans Stack. Cependant elle existe dans la classe Vector. L'affichage le contenu d'une pile n'est pas une action importante de la gestion d'une pile. Au lieu de perdre le temps de développer une telle méthode, pourquoi ne pas utiliser celle qui existe dans la super classe Vector? Supposons qu'une pile contient 7 journées d'une semaine dont on a empilé successivement les journées "Lundi", "Mardi", …, "Dimanche". La méthode suivante permet d'afficher le contenu de la pile : IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 114 static void afficher( Stack unePile, String message) { if ( unePile.empty() ) System.out.println("La pile est vide. Sa taille est " + unePile.size()); else { System.out.println("Contenu de la pile " + message + " : "); System.out.println( unePile ); } System.out.println(); } Comme toString() n'esiste pas dans Stack mais elle existe dans Vector. Java utilise la méthode toString() de la classe Vector pour afficher le contenu de la pile : Contenu de la pile apres avoir empile les 7 journees : [Lundi, Mardi, Mercredi, Jeudi, Vendredi, Samedi, Dimanche] Conclusion : on profite (réutilise) la méthode toString() de la classe Vector. Héritage simple : une classe peut hériter d'une seule super-classe classe Triangle avec 3 côtés (côté1, côté2, côté3) Triangle rectangle Triangle isocèle Triangle équilatéral (3 côtés égaux) Un triangle équilatéral est un triangle isocèle. Un triangle isocèle est aussi un triangle. etc . . . IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 115 Java permet d'implémenter facilement l'héritage simple. Ce type d'héritage est fréquent dans les classes prédéfinies de Java: La seule super classe de Stack est Vector. La seule super classe de Vector est Object. Notez qu’une super-classe (exemple Number dans java.lang) peut disposer de plusieurs sous-classes : Integer, Double, Float, Long. Héritage multiple: une classe peut hériter de plus d'une super-classe Ce concept est permis en C++ mais non en Java. Java utilise la notion de l'interface (matière vers la fin de session) pour contourner ce problème. Surdéfinir (surcharger, overloading) La surdéfinition permet au programmeur de déclarer plusieurs méthodes ou constructeurs ayant le même nom, mais ayant des paramètres différents ou du genre différent (avec return, de type void). On a déjà vu fréquemment des constructeurs sans paramètres, constructeurs avec paramètres qui portent le même nom (de la classe) : public Rectangle() { } // constructeur d'un rectangle public Rectangle(int lo, int la) {. . . } // constructeur avec paramètres La méthode afficher est aussi fréquemment surdéfinie dans les projets: public void afficher(int nombre, int nbCol) // afficher un nombre avec tant de colonnes public void afficher(String nom) // afficher un objet en connaissant son nom public void afficher(Vector v, String message) // afficher le contenu d'un vecteur … Redéfinir (overriding): Avec l'héritage, il est utile, dépendant des besoins, de redéfinir des méthodes existantes d'une super-classe. C'est le cas des deux méthodes toString() et equals qui sont souvent redéfinies dans la plupart des sous-classes d'Object . La méthode toString() est redéfinie dans String, Integer. Par cette raison, l'affichage suivant fonctionne parfaitement : String souhait = "Bonne chance!" ; Vector<Integer> v = new Vector<Integer> (); v.addElement ( new Integer (23) ); v. addElement ( new Integer (15); IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 116 System.out.println(souhait); System.out.println("Contenu de v : " + v ); Par contre, toString() n'est pas redéfinie dans StringTokenizer. Java utilise alors la méthode toString() de la classe Object pour afficher une chaîne décomposable en mots : StringTokenizer st = new StringTokenizer( "Bonsoir tout le monde "); System.out.println("La chaîne est : " + st ); L'affichage est du genre : java.util.StringTokenizer@etc …. qui n'est pas du tout la chaîne voulue. Avec String ch1 = "Bonsoir", ch2 = "bonsoir", ch3 = "Bonsoir"; on savait que : ch1.equals(ch2) retourne false (pas le même contenu) ch1.equals(ch3) retourne true (même contenu). Les résultats sont parfaits car la méthode equals a été redéfinie dans String. Avec Integer n1 = new Integer(25), n2 = new Integer(77), n3 = new Integer (25); on a : n1.equals(n2) retourne false (pas le même contenu) n1.equals(n3) retourne true (même contenu). Les résultats sont parfaits car la méthode equals a été redéfinie dans Integer. Cependant, avec : StringTokenizer st1 = new StringTokenizer( "Bonsoir"), st2 = new StringTokenizer( "Bonsoir"); on a : st1.equals(st2) retourne false (pas le même contenu!) IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 117 Le résultat est erroné car la méthode equals n'a pas été redéfinie dans StringTokenizer Java utilise alors la méthode equals de la super classe Object qui retourne false quand ce n’est pas la même référence : Dans Object, selon une source de Java : public boolean equals (Object obj) { return this == obj ; } Conclusion: pour utiliser convenablement des méthodes toString, equals, etc . . . d'une super classe, il est utile de les redéfinir dans la sous-classe. Remarque: La méthode equals est redéfinie dans Vector dans les dernières versions de JDK alors qu’elle n’est pas redéfinie dans VJ++. Avec : Vector<Integer> v1 = new Vector<Integer>(); v1.addElement ( new Integer (10) ); Vector<Integer> v2 = new Vector<Integer> (); v2.addElement ( new Integer (10) ); on a : v1.equals(v2) retourne true (dernières versions de JDK ). v1.equals(v2) retourne false (sous VJ++) Exemple de redéfinition des 2 méthodes toString() et equals: Pour les besoins pédagogiques, les champs longueur et largeur de la classe Rectangle sont de type int pour l'exemple (en programmation, on ne compare pas deux réels avec reel1 == reel2, on doit travailler avec un epsilon près. C'est une notion de la présentation interne des nombres qui n'est pas notre intérêt pour comprendre l'héritage!). public class Rectangle { private int longueur, largeur; . . . constructeurs + autres méthodes . . . public int perimetre() { return 2 * (longueur + largeur) ; } // redéfinir toString() : conversion d'un rectangle en String public String toString() { return "Rectangle : <" + longueur + ", " + largeur + ", " + perimetre() + ">\n"; IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 118 } /* redéfinir equals : l'objet courant est-il égal un autre? Il y a plusieurs manières de programmer equals. C’est un sujet de réflexion qu’on va aborder vers la fin de session */ public boolean equals ( Object autre ) { Rectangle rect =(Rectangle) autre; return longueur == rect.longueur && largeur == rect.largeur; } . . . } Polymorphisme : Le mot polymorphisme est issu d'un mot grec signifiant "qui possède plusieurs formes" (la lampe magique d'Aladin : Maître, sous quelle forme voulez-vous que je me transforme?). En JAVA, le but de polymorphisme est de permettre d'identifier clairement lequel des champs, lequel des objets, laquelle des méthodes qu'on travaille lors d'une possibilité de confusion dans leur utilisation. La redéfinition d'une méthode fait partie de polymorphisme. Auto-référence avec le mot this (qui fait partie de polymorphisme) this En Java, désigne l'objet courant. On a déjà vu le constructeur d'un rectangle comme le suivant : public Rectangle (int lo, int la) { longueur = lo ; largeur = la ; } L'instruction Rectangle r = new Rectangle(12, 8); construit un rectangle r de longueur 12 et de largeur 8. On n'a pas de problème. Le programmeur généralise l'écriture de ce constructeur comme le suivant : public Rectangle (int longueur, int largeur) { longueur = longueur ; largeur = largeur ; IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 119 } en espérant que Java comprenne que longueur à gauche de l'affectation est la longueur du rectangle courant (à créer) tandis que celle à droite de l'affectation est un paramètre. Malheureusement, Java réagit très mal à cette écriture. Il faut alors utiliser l'auto référence this qui représente l'objet courant : public Rectangle (int longueur, int largeur) { this.longueur = longueur ; this.largeur = largeur ; } Exemple 2 sur this : retourner l'objet courant: Supposons que la classe Rectangle dispose déjà d'une méthode permettant de calculer et retourner le périmètre : public int perimetre() { return 2 * (longueur + largeur) ; } Écrivez une méthode permettant de déterminer et de retourner le rectangle ayant le plus grand paramètre entre l'objet courant et un autre rectangle. public Rectangle plusGrandPerimetre (Rectangle autre) { if ( autre.perimetre() > perimetre() ) return autre ; else return this ; } Exemple 3 sur this : appel d’un constructeur dans la même classe: Supposons que la classe Rectangle dispose déjà du constructeur à 2 paramètres, par exemple : public Rectangle (int lo, int la) { longueur = lo ; largeur = la ; } Dans la même classe Rectangle, on peut avoir des constructeurs suivants : // par défaut : longueur et largeur valent zéro public Rectangle () { this (0, 0); IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 120 } // un rectangle où longueur = largeur = cote (un carré) public Rectangle (int cote) { this (cote, cote); } // un rectangle construit à partir d'un autre rectangle public Rectangle (Rectangle autre) { this (autre.longueur, autre.largeur); } Résumé sur this : Il y a trois formes d'utilisation de this : 1. this.champ => champ de donné de l'objet courant 2. this( . . .) => première instruction dans un constructeur pour appeler un autre constructeur de la même classe 3. return this ; => retourner l'objet courant Le mot clé super (venant de super classe) (fait partie aussi de la polymorphisme) On utilise souvent le mot super dans les 2 situations suivantes : 1. première instruction d'un constructeur d'une sous-classe pour appeler un constructeur approprié de la super classe; 2. appel d'une méthode de la super classe quand la sous-classe dispose d'une méthode portant le même nom. Exemple sur this et super: // classe RectangleEntier public class RectangleEntier { private int longueur ; // non accessible dans les sous-classes IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 121 /* champ protégé : - accessible dans les sous-classes - accessible aussi des autres classes du même paquet */ protected int largeur ; // revoir le this public RectangleEntier(int longueur, int largeur) { this.longueur = longueur; this.largeur = largeur ; } // revoir le this public RectangleEntier() { this (10, 8); // valeurs par défaut } // revoir le this public RectangleEntier(int cote) { this (cote, cote); } /* une parmi plusieurs manières de redéfinir equals(...) de Object Il y a des points pour, points contre d'une manière à l'autre On va aborder vers la fin de la session */ public boolean equals(Object autre) { if (this == autre) return true; if (this.getClass() == ((RectangleEntier) autre).getClass()) { return longueur == ((RectangleEntier) autre).longueur && largeur == ((RectangleEntier) autre).largeur; } return false; } // redéfinition de toString() public String toString() { return "<longueur: " + longueur + ", largeur : " + largeur + ", perimetre: " + perimetre() + ">\n"; IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 122 } public int perimetre() { return 2 * (longueur+largeur); } public int getLongueur() { return longueur; } } /* classe RectangleColore dérivée de RectangleEntier exemple sur super : - appel d'un constructeur d'une super-classe - appel d'une méthode d'une super-classe */ public class RectangleColore extends RectangleEntier { private int couleurInterieure, couleurBordure; public RectangleColore(int lo, int la, int cI, int cB) { /* - appel d'un constructeur d'une super-classe il faut être première instruction */ super(lo, la); couleurInterieure = cI; couleurBordure = cB; } public int surface() { // accès au champ PROTÉGÉ largeur est faisable return getLongueur() * largeur; } public String toString() { // appel d'une méthode d'une super-classe return super.toString() + "<surface: " + surface() + ", coul. interieure : " + couleurInterieure + ", coul. bordure : " + couleurBordure + ">\n"; } IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 123 } // classe CarreVisible dérivée de RectangleEntier public class CarreVisible extends RectangleEntier { private boolean estVisible ; public CarreVisible(int cote, boolean estVisible) { super(cote); this.estVisible = estVisible; } public double diagonale() { return getLongueur() * Math.sqrt(2.0); } public String toString() { if (estVisible) return "\nJe suis un carre visible : " + super.toString() + "<diagonale : " + diagonale() + ">\n"; else return "\nJe ne suis pas un carre visible!\n"; } } /** * Fichier TestSuper.java * * super classe RectangleEntier * * / \ * classe dérivée classe dérivée * RectangleColore CarreVisible * * Tester le bon fonctionnement de this, super, redéfinition des méthodes */ IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 124 public class TestSuper { public static void main (String[] args) { RectangleEntier re1 = new RectangleEntier(12,10); System.out.println("Infos de re1 : " + re1); RectangleEntier re2 = new RectangleEntier(9); System.out.println("Infos de re2 : " + re2); System.out.println("re1.equals(re2) vaut : " + re1.equals(re2)); RectangleColore rc1 = new RectangleColore(12,10, 5, 12); System.out.println("Infos de rc1 : " + rc1); System.out.println("re1.equals(rc1) vaut : " + re1.equals(rc1)); CarreVisible cv1 = new CarreVisible(10, true); System.out.println(cv1); CarreVisible cv2 = new CarreVisible(7, false); System.out.println(cv2); } } /* Exécution: Infos de re1 : <longueur: 12, largeur : 10, perimetre: 44> Infos de re2 : <longueur: 9, largeur : 9, perimetre: 36> re1.equals(re2) vaut : false Infos de rc1 : <longueur: 12, largeur : 10, perimetre: 44> <surface: 120, coul. interieure : 5, coul. bordure : 12> re1.equals(rc1) vaut : true Je suis un carre visible : <longueur: 10, largeur : 10, perimetre: 40> <diagonale : 14.142135623730951> Je ne suis pas un carre visible! */ IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 125 Classe Stack dérivée de Vector : classe Object public String toString(); public boolean equals(Object autre); . . . etc . . . Classe Vector<E> void addElement(E obj) Adds the specified component to the end of this vector, increasing its size by one. E elementAt(int index) Returns the component at the specified index. boolean equals(Object o) Compares the specified Object with this Vector for equality. int indexOf(Object elem) Searches for the first occurence of the given argument, testing for equality using the equals method. void insertElementAt(E obj, int index) Inserts the specified object as a component in this vector at the specified index. boolean isEmpty() Tests if this vector has no components. boolean removeElement(Object obj) Removes the first (lowest-indexed) occurrence of the argument from this vector. int size() Returns the number of components in this vector. String toString() Returns a string representation of this Vector, containing the String representation of each element. etc … . . . autres méthodes ... Sous-classe Stack de Vector IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 126 La classe Stack (une pile) qui est dérivée de la classe Vector. Sur le site de Sun, on y trouve : public class Stack extends Vector => Stack est étendue (dérivée) de la classe Vector. “The Stack class represents a last-in-first-out (LIFO) stack of objects. It extends class Vector with five operations that allow a vector to be treated as a stack.” : La classe Stack représente le concept LIFO (dernière entrée, première sortie : comme une pile d’assiettes). Elle est étendue de la classe Vector avec 5 opérations permettant de gérer une pile. Constructor Summary : résumé du constructeur Stack() Creates an empty Stack : création d’une pile VIDE (pas d’éléments) Method Summary : résumé des méthodes boolean empty() Tests if this stack is empty : tester si Oui ou Non la pile soit vide (empty). E peek() Looks at the object at the top of this stack without removing it from the stack. Consulter l’objet au sommet de la pile sans le retirer. E pop() Removes the object at the top of this stack and returns that object as the value of this function. Supprimer (retirer) l’objet au sommet de la pile et retourne cet objet (de la classe Object) comme résultat de la méthode. E push(E item) Pushes an item onto the top of this stack. Empiler (ajouter au sommet) d’un objet et retourner l’objet ajouté comme résultat de la méthode. int search(Object o) Returns the 1-based position where an object is on this stack. Retourner un indice pour l’objet o trouvé dans la pile ou -1 si non trouvé. IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 127 Method Detail : détails de ces méthodes push public E push(E item) Pushes an item onto the top of this stack. This has exactly the same effect as: addElement(item) empiler un item (un Élément) au sommet de la pile. Elle a le même effet que la méthode de sa super-classe : addElement(item) Parameters: item - the item to be pushed onto this stack. l’item à ajouter au sommet de la pile Returns: the item argument (l’argument item) l’argument de la méthode pop public E pop() Removes the object at the top of this stack and returns that object as the value of this function. Supprimer (retirer) l’élément au sommet de la pile et retourne cet objet (de la classe Object) comme résultat de la méthode. Returns: The object at the top of this stack (the last item of the Vector object). L’objet au sommet de la pile (qui est le dernier élement du vecteur). peek public E peek() Looks at the object at the top of this stack without removing it from the stack. Consulter l’objet au sommet de la pile sans le retirer. Returns: the object at the top of this stack (the last item of the Vector object). L’objet au sommet de la pile (qui est le dernier element du vecteur). empty public boolean empty() Tests if this stack is empty. Tester si Oui ou Non la pile soit vide (empty). IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 128 Returns: true if and only if this stack contains no items; false otherwise. Vrai si et seulement si la pile ne contient aucun element, faux autrement. search public int search(Object o) Returns the 1-based position where an object is on this stack. If the object o occurs as an item in this stack, this method returns the distance from the top of the stack of the occurrence nearest the top of the stack; the topmost item on the stack is considered to be at distance 1. The equals method is used to compare o to the items in this stack. (Traduction « libre » : la méthode retourne – 1 si l’objet est non trouvé et la distance vs le sommet si trouvé. Notez que la distance du sommet vaut 1 (ce n’est pas l’indice 0). Cette recherche est basée sur la comparaison du contenu utilisant la redéfinition de la méthode equals). Ainsi, pour vérifier si un objet d’une classe C fait partie d’une pile, il faut s’assurer que la méthode equals est redéfinie dans la classe C) Parameters: o - the desired object. Returns: the 1-based position from the top of the stack where the object is located; the return value -1 indicates that the object is not on the stack. Voir quelques exemples aux prochaines pages pour comprendre la méthode de recherche search. Observations à travers ces schémas : les méthodes toString() et equals() ont été redéfinies dans la super-classe Vector mais non dans Stack. Ainsi : - l’affichage du contenu d’une pile (classe Stack) utilisera la méthode toString() de Vector - la comparaison du contenu de deux vecteurs utilisera la méthode equals redéfinie dans Vector. Cette méthode est de nouveau basée sur la redéfinition de equals dans la classe contenant les objets d’une pile, d’un vecteur. - la redéfinition de equals permet d’utiliser correctement la méthode search. IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 129 Exemple 1 sur la classe Stack : /* Fichier Pile1.java Exemple d'utilisation de la sous-classe Stack (notion de pile) dérivée de la classe Vector */ import java.util.*; public class Pile1 { public static void main (String[] args) {String[] pourTester = { "Bonsoir", "monde", "tout", "pile", "file", "le"}; Stack<String> pile = new Stack<String>(); // construire une pile VIDE if (pile.empty()) // si la pile est VIDE System.out.println("La pile est vide au debut "); // empiler : ajouter au sommet de la pile seulement 5 des 6 chaînes pile.push( pourTester[1]); pile.push( pourTester[5]); pile.push( pourTester[2]); pile.push( pourTester[0]); pile.push( pourTester[4]); // utiliser toString() de la super-classe Vector System.out.println("Le contenu de la pile :\n" + pile ); System.out.println("\n"); // peek() => Consulter (sans retirer) le sommet System.out.println("Au sommet de la pile, on trouve : " + pile.peek()); // pop() : retirer et retourner le sommet : System.out.print("On retire maintenant cette chaine de la pile "); String auSommet = pile.pop(); System.out.println("qui est " + auSommet); // afficher le contenu final de la pile : System.out.println("\nLe contenu final de la pile :\n" + pile); // la recherche : System.out.println("Indices retournes par la methode search pour" + " rechercher "); for (int i = 0; i < pourTester.length ; i++) System.out.println( pourTester[i] + " : " + pile.search(pourTester[i])); System.out.println("\n\nAu revoir!"); } } IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 130 /* Exécution: La pile est vide au debut Le contenu de la pile : [monde, le, tout, Bonsoir, file] Au sommet de la pile, on trouve : file On retire maintenant cette chaine de la pile qui est file Le contenu final de la pile : [monde, le, tout, Bonsoir] Indices retournes par la methode search pour rechercher Bonsoir : 1 monde : 4 tout : 2 pile : -1 file : -1 le : 3 Au revoir! */ Exercice : Programmez vous-même la classe Pile qui joue le même rôle que la classe Stack. Tester le bon fonctionnement de votre classe Pile avec : a) la création de divi100 : la pile des diviseurs de 100. On affiche son contenu. b) la création de divi450 : la pile des diviseurs de 450. On affiche son contenu. c) la création et l’affichage de diviCommun : la pile des diviseurs communs de 100 et de 450. Conseils : Notez que Pile est dérivée de Vector. On devrait alors d’importer la classe Vector du paquet java.util et on a le droit d’utiliser des méthodes disponibles dans la classe Vector. Une solution possible : /** * Programmer soi-même Stack.java * On ne traite pas les exceptions au niveau IFT 1170 * (C'est une des matières du IFT 1176) * */ import java.util.*; IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 131 public class Pile<E> extends Vector<E> { /** * Programmer soi-même Stack.java * On ne traite pas les exceptions au niveau IFT 1170 * (C'est une des matières du IFT 1176) * */ import java.util.*; class Pile<E> extends Vector<E> { // comme empty() public boolean estVide() { return size() == 0; } // comme push(E item) public void empiler( E item) { addElement (item); } // comme peek() public E consulterSommet() { return elementAt( size()-1 ); } // comme pop() public E depiler() { E auSommet = elementAt( size() - 1 ); removeElementAt (size() - 1 ); return auSommet; } // comme search(Object obj) public int recherche( Object obj) { if ( lastIndexOf (obj) > - 1 ) return size() - lastIndexOf (obj); else return - 1; } } IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 132 /** * Fichiers Pile.java (à peu près comme Stack.java) * Pile2.java : tester la classe Pile.java */ public class Pile2 { // création de la pile des diviseurs d'un nombre static Pile<Integer> creerPile(int nombre) { Pile<Integer> diviNombre = new Pile<Integer>(); for (int candidat = 1 ; candidat <= nombre ; candidat++) if ( nombre % candidat == 0 ) diviNombre.empiler( candidat ); return diviNombre; } // affichage du contenu d'une pile avec message static void afficher(Pile<Integer> p, String message) { System.out.print("La pile des diviseurs " + message ); if (p.estVide()) System.out.println(" est VIDE"); else System.out.println(" :\n" + p); System.out.println(); } // Cette méthode foncionne avec n'importe quel objet : static boolean faitPartie( Object obj, Pile<Integer> unePile ) { for (int i = 0 ; i < unePile.size() ; i++) if ( obj.equals( unePile.elementAt(i)) ) return true; return false; } // une solution possible sans utiliser la méthode search static Pile<Integer> creerPile( Pile<Integer> pile1, Pile<Integer> pile2) { Pile<Integer> commune = new Pile<Integer>(); for (int i = 0 ; i < pile1.size() ; i++) if ( faitPartie( pile1.elementAt(i), pile2 )) commune.empiler( pile1.elementAt(i) ); return commune; } IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 133 public static void main (String[] args) { final int NOMBRE1 = 100, NOMBRE2 = 450; Pile<Integer> diviN1 = creerPile(NOMBRE1), diviN2 = creerPile(NOMBRE2); afficher(diviN1, "de " + NOMBRE1); afficher(diviN2, "de " + NOMBRE2); Pile<Integer> diviN1N2 = creerPile(diviN1, diviN2); afficher(diviN1N2, "communs de " + NOMBRE1 + " et " + NOMBRE2); } } /* Exécution: La pile des diviseurs de 100 : [1, 2, 4, 5, 10, 20, 25, 50, 100] La pile des diviseurs de 450 : [1, 2, 3, 5, 6, 9, 10, 15, 18, 25, 30, 45, 50, 75, 90, 150, 225, 450] La pile des diviseurs communs de 100 et 450 : [1, 2, 5, 10, 25, 50] */ Remarques sur la redéfinition de toString() et equals() : A) Cas d’une classe où ces deux méthodes sont redéfinies : Integer Constrctor Summary : résumé des 2 constructeurs Integer(int value) Constructs a newly allocated Integer object that represents the specified int value. Integer(String s) Constructs a newly allocated Integer object that represents the int value indicated by the String parameter. Method Summary : quelques méthodes dont la redéfinition de …. double doubleValue() Returns the value of this Integer as a double. boolean equals(Object obj) Compares this object to the specified object. int intValue() Returns the value of this Integer as an int. long longValue() Returns the value of this Integer as a long. IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 134 static int parseInt(String s) Parses the string argument as a signed decimal integer. short shortValue() Returns the value of this Integer as a short. String toString() Returns a String object representing this Integer's value. L’affichage du contenu d’une pile des « Integer », d’un « Integer » avec System.out.print fonctionne très bien. les méthodes relatives à la recherche seront basées sur equals qui a été redéfinie : Java va chercherv selon le vecteur des De plus, toutes la fonction contenu. B) Cas d’une classe que ces deux méthodes ne sont pas du tout redéfinies : StringTokenizer La classe (chaîne decomposable en mots) du paquet Java.util (le même pour Vector et Stack). java.lang.Object | +-java.util.StringTokenizer Constructor Summary : quelques constructeurs StringTokenizer(String str) Constructs a string tokenizer for the specified string. StringTokenizer(String str, String delim) Constructs a string tokenizer for the specified string. Method Summary int countTokens() Calculates the number of times that this tokenizer's nextToken method can be called before it generates an exception. boolean hasMoreElements() Returns the same value as the hasMoreTokens method. boolean hasMoreTokens() Tests if there are more tokens available from this tokenizer's string. Object nextElement() Returns the same value as the nextToken method, except that its declared return value is Object rather than String. String nextToken() Returns the next token from this string tokenizer. String nextToken(String delim) Returns the next token in this string tokenizer's string. IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 135 L’exemple suivante démontre le non fonctionnement de l’affichage et de la recherche. /** * * * * * * * * * * * * * * * * */ Par LVN pour IFT 1170 (Révisé par MR) Classes : Employe dont la méthode equals est redéfinie Pile3 : mes commentaires sur la recherche d'un élément (d'un objet) dans 1 vecteur, dans une pile Conclusions importance : - Dans une classe dont la méthode equals n'est pas du tout redéfinie (exemple : StringTokenizer) les méthodes de recherche pré-implémentées (search, indexOf, contains, ...) ne donnent pas de bons résultats - Pour utiliser correctement la recherche : . s'assurer que equals a été redéfinie (Integer, String, ...) . ou redéfinir nous-même cette méthode (exemple dans Employe de cet exemple) import java.util.*; class Employe { private String NAS; private int age; public Employe(String NAS, int age) { this.NAS = NAS; this.age = age; } public String toString() { return "NAS : " + NAS + " et age = " + age + " ans\n"; } public boolean equals (Object autre) { Employe emp = (Employe) autre; return NAS.equals(emp.NAS) ; } } public class Pile3 { static Stack<String> creer(String ch1, String ch2, String ch3, String ch4) { Stack<String> p = new Stack<String>(); p.push(ch1); p.push(ch2); p.push(ch3); p.push(ch4); return p; } /* Pas de type déclaré pour les éléments des Stack passées en IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 136 * paramètres car la méthodes sera appelée avec des Stack contenant * des éléments de types différents à chaque appel. */ static void afficher(Stack pile, String message) { if (pile.empty()) System.out.println("La pile " + message + " est VIDE\n"); else System.out.println("Contenu de la pile " + message + ":\n" +pile ); } static Stack<StringTokenizer> creerMots(String ch1, String ch2, String ch3, String ch4) { Stack<StringTokenizer> p = new Stack<StringTokenizer>(); p.push(new StringTokenizer(ch1)); p.push(new StringTokenizer(ch2)); p.push(new StringTokenizer(ch3)); p.push(new StringTokenizer(ch4)); return p; } /* Pas de type déclaré pour les éléments des Stack passées en * paramètres pour la même raison que pour afficher. */ static Stack commune(Stack a, Stack b) { Stack<Object> c = new Stack<Object>(); for (int i = 0; i < a.size() ; i++) if ( b.search(a.elementAt(i)) > -1 ) c.push(a.elementAt(i)); return c; } static void demo1Search(){ System.out.println("Premiere demonstration\n"); Stack<String> pile1 = creer("Bonsoir", "tout", "le", "monde"), pile2 = creer("Bonjour", "le", "monde", " de Java"); afficher(pile1, " # 1 "); afficher(pile2, " # 2 "); afficher( commune(pile1, pile2), " commune de pile # 1 et pile # 2 "); } static void afficher(StringTokenizer chaine) { while (chaine.hasMoreElements()) System.out.print(chaine.nextElement() + " "); } static void afficher(String message, Stack<StringTokenizer> pile ) { if (pile.empty()) System.out.println("La pile " + message + " est VIDE"); else { System.out.println("Contenu de la pile " + message +":\n"); for (int i = 0 ; i < pile.size() ; i++) { IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 137 StringTokenizer chaine = pile.elementAt(i); afficher(chaine); System.out.print(" "); } System.out.println(); } } static void demo2Search(){ System.out.println("\nDeuxieme demonstration\n"); Stack<StringTokenizer> pile1 = creerMots("Bonsoir", "tout", "le", "monde"), pile2 = creerMots("Bonjour", "le", "monde", " de Java"); afficher(pile1, " # 1 "); afficher(pile2, " # 2 "); afficher( commune(pile1,pile2), " commune de pile # 1 et pile # 2 "); afficher( " # 1 ", pile1); afficher( " # 2 ", pile2); afficher( commune(pile1, pile2), " commune de pile # 1 et pile # 2 "); } static void demo3Search(){ System.out.println("\nTroisieme demonstration\n"); Stack<Employe> employe = new Stack<Employe>(); employe.push( new Employe("123 456 789", 25) ); employe.push( new Employe("111 222 333", 44 )); employe.push( new Employe("456 987 555", 30 )); System.out.println("Pile des employes :\n" + employe); Employe aChercher = new Employe("456 987 555", 30 ); System.out.println("\nLa recherche de " + aChercher); System.out.println("1. avec search : " + employe.search(aChercher)); System.out.println("2. avec indexOf : " + employe.indexOf(aChercher)); System.out.println("3. avec lastIndexOf : " + employe.lastIndexOf(aChercher)); System.out.println("4. avec contains : " + employe.contains(aChercher)); } public static void main (String[] args) { demo1Search(); demo2Search(); demo3Search(); } } IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 138 /* Exécution: Premiere demonstration Contenu de la pile # 1 : [Bonsoir, tout, le, monde] Contenu de la pile # 2 : [Bonjour, le, monde, de Java] Contenu de la pile commune de pile # 1 et pile # 2 : [le, monde] Deuxieme demonstration Contenu de la pile # 1 : [java.util.StringTokenizer@16, java.util.StringTokenizer@19, java.util.StringTok enizer@1c, java.util.StringTokenizer@1f] Contenu de la pile # 2 : [java.util.StringTokenizer@24, java.util.StringTokenizer@27, java.util.StringTok enizer@2a, java.util.StringTokenizer@2d] La pile commune de pile # 1 et pile # 2 est VIDE Contenu de la pile # 1 : Bonsoir tout le monde Contenu de la pile # 2 : Bonjour La pile le monde de Java commune de pile # 1 et pile # 2 est VIDE Troisieme demonstration Pile des employes : [NAS : 123 456 789 et age = 25 ans , NAS : 111 222 333 et age = 44 ans , NAS : 456 987 555 et age = 30 ans ] La recherche de NAS : 456 987 555 et age = 30 ans 1. 2. 3. 4. avec avec avec avec search indexOf lastIndexOf contains : : : : 1 2 2 true */ IFT 1170, été 2009 Chapitre 8 : Héritage simple et polymorphisme 139