Dans cette série, vous vous familiariserez avec le typage explicite dans des hiérarchies de classes. Vous étudierez également l'utilité des classes et méthodes abstraites dans la mise en oeuvre du polymorphisme ainsi que l'usage des modificateurs abstract et final. Vous commencerez également à étudier le matériel fourni pour le mini-projet 2. >Attention, la prise en main de ce matériel ne doit pas se faire au détriment des exercices. Il y a beaucoup d'explications mais il s'agit pour l'essentiel d'une «visite guidée» de la maquette fournie.
Le but de cet exercice est de reprendre un exemple de "figures géométriques" pour illustrer la notion de polymorphisme. Nous utiliserons une collection hétérogène de figures géométriques.
Dans un nouveau fichier FiguresGeometriques2.java, commencez par recopier les définitions de FiguresGeometriques.java.
On souhaite maintenant travailler avec un tableau de figures géométriques contenant à la fois des rectangles et des cercles.
[Essayez de le faire par vous même avant de regarder la solution qui suit]
Sans polymorphisme, nous serions obligés de créer deux tableaux différents, puisque l'on manipule deux types d'objets.
On souhaiterait maintenant plutôt pouvoir écrire notre programme principal comme suit :
class FiguresGeometriques2 { public static void main(String[] args) { Figure[] f = new Figure[3]; f[0] = new RectangleColore(1.2, 3.4, 12.3, 43.2, 4); f[1] = new Cercle(2.3, 4.5, 12.2); f[2] = new Rectangle(1.3, 3.6, 2.3, 56.2); for (int i = 0; i < f.length; i++){ f[i].affiche(); System.out.println("La surface de cette forme est :" + f[i].surface()); } } } |
Comme la résolution dynamique des liens se fait automatiquement en Java, ceci ne pose en fait aucun problème particulier, et le code ci-dessus est presque correct.
Nous avons cependant un petit problème à résoudre.
[Essayez de le découvrir par vous même avant de regarder la solution qui suit]
En fait, nous souhaitons ici faire afficher aussi la surface des figures. Cependant, la méthode surface n'est pas définie dans Figure, donc si vous compilez le programme tel qu'il est, vous obtiendrez un message d'erreur du type
cannot resolve symbol symbol : variable surface () location: class Figure f[i].surface());
On aimerait donc définir le calcul de surface dans la classe Figure, mais cela pose un second problème: la surface d'un cercle et celle d'un rectangle ne se calculent pas de la même manière.
La solution consiste ici à déclarer la méthode surface commme abstraite dans la classe Figure
class Figure { private double x; // abscisse du centre private double y; // ordonnée du centre public Figure(double x, double y) { this.x = x; this.y = y; } public abstract double surface(); public void affiche() { System.out.println("centre = (" + x + ", " + y + ")"); } public double getX() { return x; } public double getY() { return y; } public void setCentre(double x, double y) { this.x = x; this.y = y; } } |
Cela ne suffit pas encore tout à fait: une classe qui contient une méthode abstraite ne peut pas être instanciée. Il faut donc également déclarer la classe Figure comme abstraite. Le code correct est donc:
abstract class Figure { ... |
Voilà, notre classe Figure est maintenant utilisable comme on le souhaite.
Vous pouvez trouver ici le code complet de
l'exemple.
Cette semaine vous devez visionner les vidéos de la semaine 4 du MOOC. Pour bien assimiler ce contenu, il est recommandé de faire les quiz des mêmes semaines: Quiz «Polymorphisme».
Note : cet exercice est facultatif si vous avez programmé l'exercice de niveau 0 par vous même.
Reprenez l'exercice 3 de la série 7: Surfaces.java. Vous avez déjà converti le programme procédural en programme orienté objet, mais on voudrait maintenant améliorer le code en utilisant le polymorphisme.
Tout d'abord, créez une super-classe commune pour Cercle et Rectangle que vous appellerez par exemple Forme. Cette classe devra déclarer la méthode abstraite calculerSurface afin de forcer les sous-classes à implémenter ce calcul.
Dans la solution actuelle, Terrain contient trois
instances de Rectangle et deux de Cercle, mais il devrait être capable de contenir
n'importe quelle combinaison de Formes. Remplacez donc les
variables d'instance r1, r2, r3, c1 et c2 par
un tableau de Forme (le constructeur de la classe Terrain donnera à ce tableau une taille maximale de votre choix). Ajoutez une nouvelle méthode
ajouterForme à Terrain afin
de permettre à l'utilisateur d'étendre son terrain.
N'oubliez pas de modifier calculerSurfaceTotale pour
prendre en compte vos modifications.
Le nouveau main devrait ressembler à ceci:
public static void main (String[] args) { // Construction d'un terrain: Terrain t = new Terrain(); t.ajouterForme(new Rectangle(100, 100)); t.ajouterForme(new Cercle(50)); t.afficherSurfaceTotale(); } |
La méthode main du programme suivant contient deux erreurs liées à la notion de typage fort. Indiquez pour chaque erreur:
Corrigez le fichier Typage.java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
class Typage { public static void main(String[] args) { A[] a = new A[5]; a[0] = new B(8); a[1] = new B(8); a[2] = new C(4, 2); a[3] = new C(4, 2); a[4] = new D(); int somme = 0; for (int i = 0; i < a.length; i++) { if (a[i] instanceof D) { System.out.println(((D) a[i]).d); } else { if (a[i] instanceof B) { somme = somme + ma(a[i]); if (a[i] instanceof C) { C c = a[i]; c.mc(); } } } System.out.println(somme); } } static int ma(B x) { return x.b; } } class A { public A() {}; } class B extends A { protected int b; public B(int b) { super(); this.b = b; } } class C extends B { protected int c; public C(int b, int c) { super(b); c = b; } public void mc() { System.out.println("Bzz.."); } } class D extends A { protected int d = 20; public D() { super(); } } |
Le programme ABCDEF ci-dessous implémente la hiérarchie de 6 classes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
class A { public A() { } } class B extends A { public B() { super(); } } class C extends B { public C() { super(); } } class D extends A { protected int d = 1; public D(int x) { super(); d = x; } public D() { } } class E extends D { public E() { super(); } } class F extends D { public F() { super(); } } class ABCDEF { public static void main(String[] args) { // Indiquez si les affectations suivantes sont correctes: A a = new A(); B b = new B(); C c = new C(); D d = new D(); E e = new E(); F f = new F(); a = b; b = a; a = (A) b; a = null; null = a; a = d; b = d; a = e; d = e; // Remplissage d'un tableau: A[] as = new A[10]; as[0] = new A(); as[1] = new B(); as[2] = new D(2); as[3] = new E(); as[4] = new C(); as[5] = new D(4); as[6] = new B(); as[7] = new F(); as[8] = new C(); as[9] = new F(); // A vous d'ajouter le code de ces deux méthodes: rechercher(as); additionner(as); } private static void rechercher(A[] as) { // A remplir } private static void additionner(A[] as) { // A remplir } } |
Dans la méthode main du programme ABCDEF, pour chaque affectation à des variables représentant des objets des classes A, B, C, D, E et F, indiquez si elle est correcte. Si ce n'est pas le cas, expliquez pourquoi.
Dans la deuxième partie de la méthode main, le tableau as (de type A[]) est rempli d'objets des classes A, B, C, D, E et F. Ecrivez le code de la méthode rechercher pour qu'elle affiche le nombre d'objets de type B qui se trouvent dans le tableau. Exemple d'exécution:
Il y a 4 instances de la classe B
Ecrivez la méthode additionner pour qu'elle affiche la somme des variables d'instance d des objets qui se trouvent dans le tableau as. Exemple d'exécution:
Somme des variables d : 9
Le programme AbstractFinal ci-dessous implémente une petite hiérarchie de 4 classes (A, B, C et D). Il y a 3 erreurs dans la méthode main et 1 erreur dans la classe D. Toutes les erreurs sont dûes à une utilisation erronée des modificateurs abstract et final. Expliquez ces erreurs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class AbstractFinal { public static void main(String[] args) { A[] tab = new A[3]; tab[0] = new A(); tab[1] = new B(); tab[2] = new C(); tab[1].b = 2; ((C)tab[2]).c = 3; } } abstract class A { int a; } class B extends A { int b; } class C extends A { final double c = 1; } abstract class D extends A { double d; int operation(int a) { return (a * 2); } abstract int calcul(int b) { } abstract int machin(); } |
Programmer la hiérarchie de classes "Rectangle coloré héritant de Rectangle" (vue en cours) en obéissant aux contraintes suivantes :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class ToStringEq { public static void main(String[] args) { System.out.println("Test 1 :"); Rectangle rect = new Rectangle(12.5, 4.0); System.out.println(rect); System.out.println(); System.out.println("Test 2: "); // le type de rect1 est RectangleColore // l'objet contenu dans rect1 est de type RectangleColore RectangleColore rect1 = new RectangleColore(12.5, 4.0, "rouge"); System.out.println(rect1); System.out.println(); System.out.println("Test 3 :"); // le type de rect2 est Rectangle // l'objet contenu dans rect2 est de type RectangleColore Rectangle rect2 = new RectangleColore(25.0/2, 8.0/2, new String("rouge")); System.out.println(rect2); System.out.println (rect1.equals(rect2)); // 1. System.out.println (rect2.equals(rect1)); // 2. System.out.println(rect1.equals(null)); // 3. System.out.println (rect.equals(rect1)); // 4. System.out.println (rect1.equals(rect)); // 5. } } |
Test 1 : Rectangle : largeur = 12.5 hauteur = 4.0 Test 2: Rectangle : largeur = 12.5 hauteur = 4.0 couleur = rouge Test 3 : Rectangle : largeur = 12.5 hauteur = 4.0 couleur = rouge true true false false false
Vous vous intéressez dans cet exercice à décrire les données d'un jeu simulant des combats de magiciens.
Dans ce jeu, il existe trois types de cartes : les terrains, les créatures et les sortilèges.
Dans un programme Magic.java, proposez (et implémentez) une hiérarchie de classes permettant de représenter des cartes de différents types.
Chaque classe aura un constructeur permettant de spécifier la/les valeurs de ses attributs. De plus, chaque constructeur devra afficher le type de la carte.
Le programme doit utiliser la conception orientée objet et ne doit pas comporter de duplication de code.
Ajoutez ensuite aux cartes une méthode afficher() qui, pour toute carte, affiche son coût et la valeur de ses arguments spécifiques.
Créez de plus une classe Jeu pour représenter un jeu de cartes,
c'est-à-dire une collection de telles cartes.
Cette classe
devra avoir une méthode piocher permettant
d'ajouter une carte au jeu. On supposera qu'un jeu comporte au plus 10 cartes.
Le jeu comportera également une méthode joue
permettant de jouer une carte. Pour simplifier, on jouera les cartes dans
l'ordre où elles sont stockées dans le jeu, et on mettra la carte
jouée à null dans le jeu de cartes.
Pour finir, dans la méthode main, constituez un jeu contenant divers types de cartes et faites afficher le jeu grâce à une méthode afficher propre à cette classe.
Par exemple la méthode main pourrait ressembler à quelque chose comme cela :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Magic { public static void main(String[] args) { Jeu maMain = new Jeu(10); maMain.piocher(new Terrain('b')); maMain.piocher(new Creature(6, "Golem", 4, 6)); maMain.piocher(new Sortilege(1, "Croissance Gigantesque", "La créature ciblée gagne +3/+3 jusqu'à la fin du tour")); System.out.println("Là, j'ai en stock :"); maMain.afficher(); maMain.joue(); } } |
qui produirait quelque chose comme :
On change de main Un nouveau terrain. Une nouvelle créature. Un sortilège de plus. Là, j'ai en stock : Un terrain bleu Une créature Golem 4/6 Un sortilège Croissance Gigantesque Je joue une carte... La carte jouée est : Un terrain bleu
Un programmeur amateur produit le code suivant pour tester ses connaissances en POO :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
class Polymorph { public static void main(String[] args) { Forme[] tabFormes = { new Cercle("rouge"), new Triangle("jaune") }; Collect formes = new Collect(10); // Une collection de formes // contenant une copie des objets definis // dans le tableau tabFormes for (int i = 0; i < tabFormes.length; ++i) formes.add(new Forme(tabFormes[i])); formes.dessine(); } } class Forme { private String couleur; public Forme(String uneCouleur) { couleur = uneCouleur; } public Forme(Forme other) { this.couleur = other.couleur; } public void dessine(){ System.out.println("Une forme " + couleur); } } class Triangle extends Forme { public Triangle(String uneCouleur) { super(uneCouleur); } public Triangle(Triangle autreTriangle) { super(autreTriangle); } public void dessine() { super.dessine(); System.out.println("toute pointue"); } } class Cercle extends Forme { public Cercle(String uneCouleur) { super(uneCouleur); } public Cercle(Cercle autreCercle) { super(autreCercle); } public void dessine() { super.dessine(); System.out.println("toute ronde"); } } class Collect { private Forme collect[]; private int index; public Collect(int indexMax) { collect = new Forme[indexMax]; index = -1; } public void add(Forme a) { if (index < collect.length - 1) { ++ index; collect[index] = a; } } public void dessine() { for (int i = 0; i <= index; ++i) collect[i].dessine(); } } |
Il s’attend à ce que son programme produise l’affichage suivant :
Une forme rouge toute rondeQuestions:
Une forme jaune
toute pointue
Les contraintes à respecter sont les suivantes :
Cette semaine, au vu des concepts présentés en cours et dans le MOOC, il est prévu que vous fassiez la partie I du tutoriel fourni pour réaliser le mini-projet 2 [Lien] (pages 1 à 17) . Please note that an automatically generated English translation [Link] , is also provided.
Les devoirs du MOOC, même s'il ne sont pas notés pour vous, sont un bon moyen de vous entraîner aussi. Vous pouvez les soumettre à un correcteur automatique (autant de fois que vous le souhaitez) et recevoir un feedback. Cette semaine vous pouvez faire le devoir de la semaines 4 («Polymorphisme»).
Dernière mise à jour: 15/11/2024 (Revision: 1.2)