Dans cette feuille d'exercice, nous approfondissons les notions de classes abstraites, d'interfaces, d'implémentations et faisons un usage très large du sous-typage et du polymorphisme. Nous utilisons également quelques collections de Java.
Exercice 1 - Visibilité, redéfinition et masquage
Que pensez vous du code suivant et, s'il compile, qu'affiche-t il?
class A { public void m1() throws Exception { System.out.println("A.m1"); } protected void m2() { System.out.println("A.m2"); } void m3() throws Exception { System.out.println("A.m3"); } private void m4() { System.out.println("A.m4"); } public static void main(String[] args) { A a = new B(); a.m1(); a.m2(); a.m3(); a.m4(); } } class B extends A { void m1() { System.out.println("B.m1"); } public void m2() throws IOException { System.out.println("B.m2"); } void m3() throws IOException { System.out.println("B.m3"); } void m4() { System.out.println("B.m4"); } public static void main(String[] args) { A a = new B(); a.m1(); a.m2(); a.m3(); a.m4(); } }
Même question pour le code suivant:
class A { A m1() { System.out.println("A A.m1()"); return this; } void m2(A a) { System.out.println("void A.m2(A)"); } void m3(A a) { System.out.println("void A.m3(A)"); } void m3(B b) { System.out.println("void A.m3(B)"); } Object m3(B b) { System.out.println("Object A.m3(B)"); } } class B extends A { B m1() { System.out.println("B B.m1()"); return this; } void m2(B b) { System.out.println("void B.m2(B)"); } void m3(A a) { System.out.println("void B.m3(A)"); } void m3(B b) { System.out.println("void B.m3(B)"); } public static void main(String[] args) { B b = new B(); A a = b; a.m2(a); a.m2(b); ((B)a).m2(a); b.m2(a); b.m2(b); b.m2((B)a); a.m3(a); a.m3(b); b.m3(a); b.m3(b); } }
Et enfin:
class A { int x; A(int x) { this.x = x; } } class B extends A { double x; B(double x) { this.x = x; } public static void main(String[] args) { A a = new B(1); System.out.println(a.x); System.out.println(((B)a).x); } }
Exercice 2 - Carrés, cercles, rectangles
Considérons des carrés (représentés par la longueur de leur côté), des cercles (représentés par leur rayon) et des rectangles (représentés par leur largeur et leur hauteur). On veut disposer d'une structure permettant de stocker des instances de carrés, de cercles et de rectangles. Ceux-ci y seront ajoutés, un par un, avec une méthode add() sur la structure, en lui passant en argument.
On veut de plus que cette structure dispose d'une méthode min() qui renvoie la forme ayant la plus petite surface. Par exemple, si la structure contient un rectangle de largeur et hauteur 2, un carré de côté 1.5 et deux cercles de rayons respectifs 1 et 2, la méthode min() doit retourner le carré.
Proposer une solution (hiérarchie de classes, interfaces ou classes abstraites) permettant de répondre à ce problème. Coder et vérifier que votre solution fonctionne.
On désire ensuite pouvoir extraire de la structure n'importe quel objet, en utilisant une méthode remove() qui accepte en argument l'objet qu'on désire ôter. Quelles sont les modifications à approrter. Coder cette nouvelle méthode et tester son fonctionnement.
Exercice 3 - Piles
Écrire une interface Stack représentant les fonctionnalités qu'on attend d'une pile d'objets. Classiquement, ces fonctionnalités sont l'ajout au sommet (void push(Object o)), le retrait au sommet (Object pop()) et le test permettant de savoir si la pile est vide (boolean isEmpty()).
Écrire une première implémentation de l'interface Stack sous la forme d'une classe ArrayStack qui stocke, en interne, les éléments de la pile dans un tableau d'objet.
Écrire une seconde implémentation de l'interface Stack sous la forme d'une classe ListStack qui stocke, en interne, les éléments de la pile dans une liste (de la classe java.util.ArrayList).
Exercice 4 - Piles de piles
Nous voulons maintenant disposer d'une fonctionnalité un peu particulière, permettant d'empiler tous les éléments d'une pile p2 sur une pile p1. Néanmoins, avec l'interface Stack dont on dispose, l'instruction p1.push(p2) a pour effet d'ajouter sur le dessus de la pile p1 un seul objet qui est la pile p2 elle-même. On voudrait avoir maintenant une autre méthode (surchargée) push(Stack s) qui, lorsqu'elle est appelée sur une pile p1 et qu'elle accepte un objet en argument qui est une pile p2, ajoute séparément chacun des éléments de la pile p2 à la pile p1, dans le même ordre où ils étaient empilés dans p2.
Complétez la hierarchie de types formée par Stack, ArrayStack et ListStack par des classes, interfaces ou classes abstraites de sorte à disposer de cette méthode push(Stack s).
Quel problème rencontre-t on lorsqu'on compile, par exemple, le code suivant:
Stack s = new ListStack(); s.push("d"); s.push("e"); ListStackableStack ss = new ListStackableStack(); ss.push("a"); ss.push("b"); ss.push("c"); ss.push(s);