Lors de la création d’une nouvelle classe en Java, vous pouvez définir la classe comme héritant d’une classe existante. L’héritage est le processus par lequel une sous-classe inclut automatiquement certains membres de la classe, y compris les types primitifs, les objets ou les méthodes, définis dans la classe parent.
À des fins d’illustration, nous faisons référence à toute classe qui hérite d’une autre classe comme une sous-classe ou classe enfant, car elle est considérée comme un descendant de cette classe. Alternativement, nous faisons référence à la classe dont l’enfant hérite comme la superclasse ou classe parent, car elle est considérée comme un ancêtre de la classe.
Lorsque vous travaillez avec d’autres types, comme les interfaces, nous avons tendance à utiliser les termes généraux sous-type et super-type. Vous verrez cela plus en détail dans le chapitre suivant.
Déclaration d’une Sous-classe
Commençons par la déclaration d’une classe et de sa sous-classe. La figure montre un exemple d’une superclasse, Mammifere
, et d’une sous-classe Rhinoceros
.
public class Mammifere { }
public final class Rhinoceros extends Mammifere { }
Nous indiquons qu’une classe est une sous-classe en la déclarant avec le mot-clé extends
. Nous n’avons pas besoin de déclarer quoi que ce soit dans la superclasse, si ce n’est de s’assurer qu’elle n’est pas marquée final
.
Un aspect clé de l’héritage est qu’il est transitif. Étant donné trois classes [X, Y, Z], si X étend Y, et Y étend Z, alors X est considérée comme une sous-classe ou descendante de Z. De même, Z est une superclasse ou ancêtre de X. Nous utilisons parfois le terme sous-classe directe ou descendant pour indiquer que la classe étend directement la classe parent. Par exemple, X est un descendant direct uniquement de la classe Y, pas de Z.
Il existe quatre niveaux d’accès : public
, protected
, package
et private
. Lorsqu’une classe hérite d’une classe parent, tous les membres public
et protected
sont automatiquement disponibles dans la classe enfant. Si les deux classes sont dans le même package, alors les membres package
sont disponibles pour la classe enfant. Enfin, les membres private
sont limités à la classe dans laquelle ils sont définis et ne sont jamais disponibles via l’héritage. Cela ne signifie pas que la classe parent ne peut pas avoir de membres private
qui peuvent contenir des données ou modifier un objet ; cela signifie simplement que la sous-classe n’y a pas d’accès direct.
Regardons un exemple simple :
public class GrosChat {
protected double taille;
}
public class Jaguar extends GrosChat {
public Jaguar() {
taille = 10.2;
}
public void afficherDetails() {
System.out.print(taille);
}
}
public class Araignee {
public void afficherDetails() {
System.out.println(taille); // NE COMPILE PAS
}
}
Jaguar
est une sous-classe ou enfant de GrosChat
, faisant de GrosChat
une superclasse ou parent de Jaguar
. Dans la classe Jaguar
, taille
est accessible car il est marqué protected
. Via l’héritage, la sous-classe Jaguar
peut lire ou écrire taille
comme s’il s’agissait de son propre membre. Cela contraste avec la classe Araignee
, qui n’a pas accès à taille
puisqu’elle n’en hérite pas.
Modificateurs de Classe
Comme les méthodes et les variables, une déclaration de classe peut avoir différents modificateurs. Le tableau ci-dessous liste les modificateurs que vous devriez connaître.
Modificateur | Description |
---|---|
final | La classe ne peut pas être étendue. |
abstract | La classe est abstraite, peut contenir des méthodes abstraites et nécessite une sous-classe concrète pour être instanciée. |
sealed | La classe ne peut être étendue que par une liste spécifique de classes. |
non-sealed | Une sous-classe d’une classe scellée permet potentiellement des sous-classes non nommées. |
static | Utilisé pour les classes imbriquées statiques définies dans une classe. |
Nous couvrirons les classes abstraites plus loin dans ce chapitre. Dans le chapitre suivant, nous couvrirons les classes scellées et non scellées, ainsi que les classes imbriquées statiques.
Pour l’instant, parlons du marquage d’une classe comme final
. Le modificateur final
empêche une classe d’être étendue davantage. Par exemple, ce qui suit ne compile pas :
public final class Rhinoceros extends Mammifere { }
public class Clara extends Rhinoceros { } // NE COMPILE PAS
Portez attention à toute classe marquée final
. Si vous voyez une autre classe qui l’étend, vous savez immédiatement que le code ne compile pas.
Héritage Simple vs. Multiple
Java prend en charge l’héritage simple, par lequel une classe ne peut hériter que d’une seule classe parent directe. Java prend également en charge plusieurs niveaux d’héritage, par lesquels une classe peut étendre une autre classe, qui à son tour étend une autre classe. Vous pouvez avoir n’importe quel nombre de niveaux d’héritage, permettant à chaque descendant d’accéder aux membres de ses ancêtres.
Pour vraiment comprendre l’héritage simple, il peut être utile de le comparer à l’héritage multiple, par lequel une classe peut avoir plusieurs parents directs. Par conception, Java ne prend pas en charge l’héritage multiple dans le langage car l’héritage multiple peut conduire à des modèles de données complexes, souvent difficiles à maintenir. Java autorise une exception à la règle de l’héritage simple, que vous verrez dans le chapitre suivant : une classe peut implémenter plusieurs interfaces.
La figure illustre les différents types de modèles d’héritage. Les éléments à gauche sont considérés comme de l’héritage simple car chaque enfant a exactement un parent. Vous pouvez remarquer que l’héritage simple n’empêche pas les parents d’avoir plusieurs enfants. Le côté droit montre des éléments qui ont un héritage multiple. Comme vous pouvez le voir, un objet Chien
a plusieurs désignations de parent.
Une partie de ce qui rend l’héritage multiple compliqué est de déterminer de quel parent hériter des valeurs en cas de conflit. Par exemple, si vous avez un objet ou une méthode définie dans tous les parents, lequel l’enfant hérite-t-il ? Il n’y a pas d’ordre naturel pour les parents dans cet exemple, c’est pourquoi Java évite ces problèmes en interdisant complètement l’héritage multiple.
Hériter d’Object
Tout au long de notre discussion sur Java dans ce livre, nous avons mentionné le mot objet de nombreuses fois—et pour une bonne raison. En Java, toutes les classes héritent d’une seule classe : java.lang.Object
, ou Object
en abrégé. De plus, Object
est la seule classe qui n’a pas de classe parent.
Vous vous demandez peut-être : “Aucune des classes que j’ai écrites jusqu’à présent n’étend Object
, alors comment toutes les classes en héritent-elles ?” La réponse est que le compilateur a automatiquement inséré du code dans toute classe que vous écrivez qui n’étend pas une classe spécifique. Par exemple, les deux exemples suivants sont équivalents :
public class Zoo { }
public class Zoo extends java.lang.Object { }
La clé est que lorsque Java voit que vous définissez une classe qui n’étend pas une autre classe, le compilateur ajoute automatiquement la syntaxe extends java.lang.Object
à la définition de la classe. Le résultat est que chaque classe gagne accès à toutes les méthodes accessibles de la classe Object
. Par exemple, les méthodes toString()
et equals()
sont disponibles dans Object
; elles sont donc accessibles dans toutes les classes. Sans être redéfinies dans une sous-classe, elles peuvent ne pas être particulièrement utiles. Nous couvrirons la redéfinition des méthodes plus loin dans ce chapitre.
D’autre part, lorsque vous définissez une nouvelle classe qui étend une classe existante, Java n’étend pas automatiquement la classe Object
. Puisque toutes les classes héritent d’Object
, étendre une classe existante signifie que l’enfant hérite déjà d’Object
par définition. Si vous regardez la structure d’héritage de n’importe quelle classe, elle se terminera toujours avec Object
au sommet de l’arbre.
Les types primitifs tels que int
et boolean
n’héritent pas d’Object
, puisqu’ils ne sont pas des classes. Grâce à l’autoboxing, ils peuvent être assignés ou passés comme une instance d’une classe wrapper associée, qui hérite d’Object
.