Classes abstraites et interfaces
Cours · programmation Java (école d'ingénieur), chapitre 10 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun
Au chapitre précédent, la classe Forme avait une méthode aire() qui renvoyait . Ce n'était qu'un pis-aller : une « forme » sans plus de précision n'a pas d'aire, et personne ne devrait jamais créer un objet new Forme("?"). Ce que l'on voulait dire, c'est que toute forme concrète doit savoir calculer son aire, sans pouvoir dire comment au niveau de Forme.
Java offre deux outils pour exprimer cela. La classe abstraite est une classe incomplète : elle factorise du code commun, mais laisse certaines méthodes à écrire aux sous-classes. L'interface va plus loin : c'est un pur contrat, une liste de méthodes qu'une classe s'engage à fournir, et une classe peut signer autant de contrats qu'elle veut. Les interfaces sont partout dans la bibliothèque Java : savoir trier des objets, les parcourir, les comparer, c'est savoir implémenter des interfaces.
Objectifs du chapitre
- Déclarer une classe et des méthodes abstraites, et comprendre ce que le compilateur exige alors des sous-classes.
- Déclarer une interface, l'implémenter (
implements), en implémenter plusieurs à la fois. - Choisir entre classe abstraite et interface.
- Utiliser les méthodes
defaultetstaticdes interfaces. - Rendre des objets triables avec
Comparable, et les trier autrement avec unComparator. - Programmer « vers l'interface », et connaître les interfaces scellées et les classes anonymes.
10.1 Classes abstraites
10.1.1 Méthodes et classes abstraites
Une méthode abstraite est une méthode déclarée sans corps, avec le modificateur abstract : abstract double aire();. Une classe qui contient une méthode abstraite doit être déclarée abstract. Une classe abstraite ne peut pas être instanciée : on ne peut pas écrire new suivi de son nom.
Reprenons les formes. La classe Forme déclare deux méthodes abstraites, que chaque forme fournira, et une méthode concrète, decrire(), écrite une fois pour toutes en s'appuyant sur les deux autres.
abstract class Forme {
abstract double aire(); // pas de corps : chaque forme le fournit
abstract String nom();
String decrire() { // méthode concrète, commune à toutes
return nom() + " d'aire " + String.format("%.3f", aire());
}
}
class Cercle extends Forme {
private final double r;
Cercle(double r) { this.r = r; }
@Override double aire() { return Math.PI * r * r; }
@Override String nom() { return "cercle"; }
}
class Rectangle extends Forme {
private final double l, h;
Rectangle(double l, double h) { this.l = l; this.h = h; }
@Override double aire() { return l * h; }
@Override String nom() { return "rectangle"; }
}
class Triangle extends Forme {
private final double a, b, c;
Triangle(double a, double b, double c) { this.a = a; this.b = b; this.c = c; }
@Override double aire() { // formule de Héron
double p = (a + b + c) / 2;
return Math.sqrt(p * (p - a) * (p - b) * (p - c));
}
@Override String nom() { return "triangle"; }
}
Avec le tableau
Forme[] dessin = { new Cercle(1.0), new Rectangle(2.0, 0.9), new Triangle(3, 4, 5) };
et une boucle qui affiche f.decrire() pour chaque forme :
cercle d'aire 3.142
rectangle d'aire 1.800
triangle d'aire 6.000
Le polymorphisme fonctionne exactement comme au chapitre 9 : decrire(), écrite dans Forme, appelle nom() et aire(), et c'est la version de l'objet réel qui répond. La différence est que Forme n'a plus à inventer une aire factice.
10.1.2 Ce que le compilateur exige
Une classe abstraite donne deux garanties, vérifiées à la compilation.
- Une classe abstraite ne peut pas être instanciée.
- Une sous-classe concrète (non abstraite) doit redéfinir toutes les méthodes abstraites dont elle hérite. Sinon, elle doit elle-même être déclarée
abstract.
Par conséquent, tout objet dont le type statique est Forme possède à coup sûr une méthode aire() effective.
Les deux erreurs correspondantes :
Forme f = new Forme();
E1.java:3: error: Forme is abstract; cannot be instantiated
Forme f = new Forme();
^
class Carre extends Forme {
private double c;
@Override double aire() { return c * c; }
}
E2.java:10: error: Carre is not abstract and does not override abstract method nom() in Forme
class Carre extends Forme {
^
Le second message est une aide précieuse : il donne le nom exact de la méthode oubliée. Dans un projet réel, ajouter une méthode abstraite à une super-classe fait apparaître la liste complète des sous-classes à compléter.
Attention
Une classe abstraite peut avoir des attributs et des constructeurs (appelés par super(...) depuis les sous-classes), et des méthodes concrètes. « Abstraite » ne veut pas dire « vide » : cela veut dire « incomplète ». On peut d'ailleurs déclarer abstract une classe sans aucune méthode abstraite, simplement pour interdire de l'instancier.
10.2 Interfaces
10.2.1 Un contrat
Une interface est un type qui déclare des méthodes sans (en général) les implémenter : ```java
interface Mesurable { double longueur(); } ` pour chacune des méthodes de l'interface. Une interface n'a ni constructeur ni attribut d'instance. Ses méthodes sont implicitement public et abstract. Une classe **implémente** une interface en le déclarant (class Segment implements Mesurable) et en fournissant une méthode public
Une interface définit un type, comme une classe : on peut déclarer une variable Mesurable m, un paramètre ou un tableau Mesurable[], et y ranger n'importe quel objet d'une classe qui implémente Mesurable. Le polymorphisme s'applique : la méthode exécutée est celle de l'objet.
La suite du cours est sur Adloun
Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.
Lire la suite du coursEn accès libre, sans inscription.