Nous avons vu comment définir une classe par composition d'autres classes. Java propose une autre technique typique des langages de classes, dite d'extension, qui permet de réutiliser une (seule) classe, en héritant de ses membres. L'héritage est un mécanisme des langages à objets particulièrement utile en génie logiciel.
Par exemple, on peut définir une classe PointColore comme une extension de Point, à l'aide de la clause extends, et de lui ajouter un champ de type java.awt.Color. La classe Point est dite parente ou sur-classe directe de PointColore, celle-ci étant dérivée, ou sous-classe directe de Point :
package geometrie; class PointColore extends Point { java.awt.Color couleur; PointColore(double x, double y, java.awt.Color couleur) { super(x, y); this.couleur = couleur; } PointColore() { super(); this.couleur = java.awt.Color.black; } }
Ses constructeurs commencent par invoquer le constructeur de la classe parente, par l'opérateur super. En effet, le nom super peut être employé avec une liste d'arguments pour invoquer explicitement un constructeur de la classe parente, celui qui accepte les mêmes types d'arguments que cette invocation de super. Une invocation de super( ... ) ne peut figurer qu'en première instruction du corps du constructeur (retenir qu'avant de créer un objet, il faut d'abord créer son « parent »). Une invocation de super n'est pas obligatoire, mais si elle n'est pas explicite, une invocation implicite de super() a toujours lieu, sans argument, ce qui suppose que la classe parente a un constructeur sans paramètre.
Tous les membres de la classe Point, c'est-à-dire ses deux
champs x et y et sa méthode translater, sont
alors hérités par PointColore (figure ) :
PointColore pc = new PointColore(1, 2, java.awt.Color.red); pc.translater(2, 2);
![]() |
De façon générale, ce mécanisme d'extension spécifié par la clause extends a deux effets :
L'héritage n'est pas systématique. Il y a d'abord une condition d'accessibilité, que nous préciserons par la suite. Par exemple, les membres privés ne sont pas hérités. Les méthodes d'instance ne sont héritées que si elles ne sont pas masquées dans la classe dérivée. Enfin, les constructeurs ne sont jamais hérités.
À l'exception de la classe Object, toute classe dérive d'une autre classe ; si la mention de l'extension est absente, ceci signifie que la classe dérive d'Object. Ceci permettra de réaliser une forme de généricité qui permet de traiter tous les objets de façon uniforme.
Comme pour les classes, il y a une notion d'extension pour les interfaces. Comme une interface déclare des méthodes, étendre une interface permet de déclarer des méthodes supplémentaires. Ceci se fait également au moyen du mot-clé extends :
interface I1 { ... } interface I2 extends I1 { ... }
On dit alors que I2 est une sous-interface directe de I1, ou qu'elle dérive de I1, et que I1 est une sur-interface directe de I2. L'effet de cette extension est que les déclarations de méthodes de I1 sont héritées par I24.2.
Une sous-interface d'une interface est considérée comme un sous-type de cette interface, ce qui permet d'utiliser une expression disposant de méthodes supplémentaires, là où seulement certaines de ces méthodes sont requises ; les affectations suivantes sont donc correctes :
I2 y = ...; I1 x = y;Dans ce cas, seules les méthodes déclarées par I1 pourront être invoquées sur x, alors que l'objet désigné par x dispose probablement d'autres méthodes. On dispose ainsi d'une notion d'abstraction graduelle.
package geometrie; interface FormeNommée extends Forme { String getNom(); }
Définissons maintenant les classes PointNommé, CercleNommé et RectangleNommé afin qu'ils réalisent FormeNommée au lieu de Forme. Le plus simple est d'en faire des extensions des classes Point, etc. Les types concrets PointNommé, CercleNommé, etc., doivent donc définir la méthode getNom. Il faut par conséquent ajouter un paramètre de type String au constructeur, et invoquer le constructeur de la classe parente, au moyen de super(x, y) :
package geometrie; class PointNommé extends Point implements FormeNommée { private String nom; PointNommé(double x, double y, String nom) { super(x, y); this.nom = nom; } public String getNom() { return nom; } }
La classe PointNommé est un sous-type de Point, qu'elle étend, et de FormeNommée, qu'elle réalise.
La classe dérivée ne peut accéder directement aux champs privés de la classe parente, mais peut éventuellement y accéder si elle hérite de méthodes d'accès. Dans l'exemple précédent, le champ x n'est pas hérité, mais la méthode getX est héritée. Par suite, si p est un point nommé, l'expression p.x est incorrecte, mais l'expresssion p.getX() est correcte.