Héritage
Dans les leçons précédentes, vous avez vu l’héritage mentionné à plusieurs reprises. Dans le langage Java, les classes peuvent être dérivées d’autres classes, héritant ainsi des champs et des méthodes de ces classes.
À l’exception de
Object
, qui n’a pas de superclasse, chaque classe en a une et une seule superclasse directe (héritage unique). En l’absence de toute autre superclasse explicite, chaque classe est implicitement une sous-classe de Object
. Les classes peuvent être dérivées de classes dérivées de classes dérivées de classes, et ainsi de suite, et finalement dérivé de la classe la plus élevée,
Object
. On dit qu’une telle classe descend de toutes les classes de la chaîne d’héritage remontant à Object
. L’idée d’héritage est simple mais puissante: lorsque vous souhaitez créer une nouvelle classe et qu’il existe déjà une classe qui inclut une partie du code que vous souhaitez, vous pouvez dériver votre nouvelle classe de la classe existante . Ce faisant, vous pouvez réutiliser les champs et méthodes de la classe existante sans avoir à les écrire (et les déboguer!) Vous-même.
Une sous-classe hérite de tous les membres (champs, méthodes et classes imbriquées) de sa superclasse. Les constructeurs ne sont pas des membres, ils ne sont donc pas hérités par les sous-classes, mais le constructeur de la superclasse peut être invoqué depuis la sous-classe.
La hiérarchie des classes de la plateforme Java
Le Object
, définie dans le package java.lang
, définit et implémente un comportement commun à toutes les classes, y compris celles que vous écrivez. Dans la plate-forme Java, de nombreuses classes dérivent directement de Object
, d’autres classes dérivent de certaines de ces classes, et ainsi de suite, formant une hiérarchie de classes.
Toutes les classes de la plateforme Java sont des descendants d’objet
En haut de la hiérarchie, Object
est la plus générale de toutes les classes. Les classes situées en bas de la hiérarchie fournissent un comportement plus spécialisé.
Un exemple d’héritage
Voici l’exemple de code pour une implémentation possible d’un Bicycle
qui a été présentée dans la leçon Classes and Objects:
Une déclaration de classe pour une classe MountainBike
qui est une sous-classe de Bicycle
pourrait ressembler à ceci:
public class MountainBike extends Bicycle { // the MountainBike subclass adds one field public int seatHeight; // the MountainBike subclass has one constructor public MountainBike(int startHeight, int startCadence, int startSpeed, int startGear) { super(startCadence, startSpeed, startGear); seatHeight = startHeight; } // the MountainBike subclass adds one method public void setHeight(int newValue) { seatHeight = newValue; } }
MountainBike
hérite tous les champs et méthodes de Bicycle
et ajoute le champ seatHeight
et une méthode pour le définir. Sauf pour le constructeur, c’est comme si vous aviez écrit une nouvelle classe MountainBike
entièrement à partir de zéro, avec quatre champs et cinq méthodes. Cependant, vous n’avez pas eu à faire tout le travail. Cela serait particulièrement utile si les méthodes de la classe Bicycle
étaient complexes et avaient pris beaucoup de temps à déboguer.
Ce que vous pouvez faire dans une sous-classe
Une sous-classe hérite de tous les membres publics et protégés de son parent, quel que soit le package dans lequel se trouve la sous-classe. Si la sous-classe est dans le même package que son parent, il hérite également des membres privés du package du parent. Vous pouvez utiliser les membres hérités tels quels, les remplacer, les masquer ou les compléter avec de nouveaux membres:
- Les champs hérités peuvent être utilisé directement, comme n’importe quel autre champ.
- Vous pouvez déclarer un champ dans la sous-classe avec le même nom que celui de la superclasse, le masquant ainsi (non recommandé).
- Vous pouvez déclarer de nouveaux champs dans la sous-classe qui ne sont pas dans la superclasse.
- Les méthodes héritées peuvent être utilisées directement telles quelles.
- Vous pouvez écrire une nouvelle méthode d’instance dans le sous-classe que a la même signature que celle de la superclasse, ce qui la remplace.
- Vous pouvez écrire une nouvelle méthode statique dans la sous-classe qui a la même signature que celle de la superclasse, la masquant ainsi.
- Vous pouvez déclarer de nouvelles méthodes dans la sous-classe qui ne sont pas dans la superclasse.
- Vous pouvez écrire un constructeur de sous-classe qui invoque le constructeur de la superclasse, soit implicitement, soit en utilisant le mot-clé
super
.
Les sections suivantes de cette leçon développeront ces sujets.
Membres privés d’une superclasse
Une sous-classe n’hérite pas des membres private
de sa classe parente. Cependant, si la superclasse dispose de méthodes publiques ou protégées pour accéder à ses champs privés, celles-ci peuvent également être utilisées par la sous-classe.
Une classe imbriquée a accès à tous les membres privés de sa classe englobante – à la fois les champs et les méthodes. Par conséquent, une classe imbriquée publique ou protégée héritée d’une sous-classe a un accès indirect à tous les membres privés de la superclasse.
Casting des objets
Nous avons vu qu’un objet est de la type de données de la classe à partir de laquelle il a été instancié. Par exemple, si nous écrivons
public MountainBike myBike = new MountainBike();
alors myBike
est de type MountainBike
.
MountainBike
est un descendant de Bicycle
et Object
. Par conséquent, un MountainBike
est un Bicycle
et est également un Object
, et il peut être utilisé partout où des objets Bicycle
ou Object
sont appelés.
L’inverse n’est pas nécessairement vrai: un Bicycle
peut être un MountainBike
, mais ce n’est pas forcément un « t ». De même, un Object
peut être un Bicycle
ou un MountainBike
, mais ce n’est pas forcément.
La diffusion montre l’utilisation d’un objet de l’un type à la place d’un autre type, parmi les objets autorisés par héritage et implémentations. Par exemple, si nous écrivons
Object obj = new MountainBike();
alors obj
est à la fois un Object
et un MountainBike
(jusqu’à ce que obj
se voit attribuer un autre objet qui n’est pas un MountainBike
). C’est ce qu’on appelle le casting implicite.
Si, par contre, nous écrivons
MountainBike myBike = obj;
nous le ferions obtenir une erreur de compilation car obj
n’est pas connu du compilateur comme étant un MountainBike
. Cependant, nous pouvons dire au compilateur que nous promettons d’assigner un MountainBike
à obj
par un casting explicite:
MountainBike myBike = (MountainBike)obj;
Cette distribution insère une vérification d’exécution que obj
se voit attribuer un MountainBike
pour que le compilateur puisse supposer en toute sécurité que obj
est un MountainBike
. Si obj
n’est pas un MountainBike
au moment de l’exécution, une exception sera levée.
instanceof
. Cela peut vous éviter une erreur d’exécution due à une distribution incorrecte. Par exemple:
if (obj instanceof MountainBike) { MountainBike myBike = (MountainBike)obj;}
Ici, l’opérateur instanceof
vérifie que obj
fait référence à un MountainBike
afin que nous puissions effectuer le cast en sachant qu’aucune exception d’exécution ne sera levée.