Héritage
Introduction
L'héritage est un concept de la programmation orientée objet.
Il permet de :
- Déterminer une hiérarchie dans les classes et les regrouper par familles.
- Factoriser des comportements et des caractéristiques communes.
- Redéfinir des comportements.
Caractéristiques du langage Java :
- Toutes classes Java hérite de
java.lang.Object
. - Une classe concrète ou abstraite ne peut hériter que d'une seule classe concrète ou abstraite.
- Une classe concrète ou abstraite peut hériter de plusieurs interfaces.
- Une interface peut hériter de plusieurs interfaces.
- Une énumération ne peut pas être héritée, ou hériter de classes, mais il peut implémenter des interfaces.
- Une annotation ne supporte pas l'héritage, mais bizarrement peut être héritée.
Mise en œuvre
Quelques mots clef sont à connaitre pour utiliser l'héritage en Java:
- protected : Niveau de visibilité protégé, visible par les classes enfants et classe du même package.
- extends : Dans la déclaration d'une classe, permet de spécifier la classe à hériter.
- implements : Dans la déclaration d'une classe, permet de spécifier les interfaces à hériter.
- final :
- Dans la déclaration d'une classe, bloque l'héritage de cette dernière (pas de classes enfants possibles).
- Dans la déclaration d'une méthode, bloque la redéfinition de méthode pour les classes enfants.
Point particulier
Héritage pour une classe :
public abstract class AbstractEntity { /* ... */ }
public class Cutomer extends AbstractEntity implements Serializable, Comparable<Customer> {
/* ... */
}
Héritage pour une interface :
public interface Repository<E> { /* ... */ }
public interface CustomerRepository extends Serializable, Repository<Customer> {
/* ... */
}
Cas d'usage
Considérons les classes suivantes:
package fr.zelmoacademy.sample;
import java.time.LocalDate;
public class Cat {
private String name;
private Float size;
private Float weight;
private LocalDate birthDate;
public Cat() {
}
public void talk() {
System.out.println("Meow");
}
public void sleep() {
System.out.println("Purrr");
}
public void huntMice() {
// ...
}
// Accesseurs & Mutateurs
// ...
}
package fr.zelmoacademy.sample;
import java.time.LocalDate;
public class Dog {
private String name;
private Float size;
private Float weight;
private LocalDate birthDate;
public Dog() {
}
public void talk() {
System.out.println("Woof");
}
public void sleep() {
System.out.println("Zzz");
}
public void sniff() {
// ...
}
// Accesseurs & Mutateurs
// ...
}
Cet exemple caricatural met en évidence des caractéristiques communes entre les deux classes. On peut donc factoriser du code dans une classe abstraite :
package fr.zelmoacademy.sample;
import java.time.LocalDate;
import java.time.Period;
// Mot clef 'abstract' pour indiquer que cette classe est abstraite
// et donc ne peut pas être instancié directement.
public abstract class Pet {
// Attributs à visibilité 'protected'
// Visible par les classes enfants et les classes du même 'package'.
protected String name;
protected Float size;
protected Float weight;
protected LocalDate birthDate;
// Constructeur par défaut
// Visibilité 'protected' recommandée.
protected Pet() {
}
// Méthode abstraite
// Les classes enfants seront obligées de définir un comportement.
public abstract void talk();
// Méthode concrète
// Les classes enfants peuvent redéfinir un comportement.
public void sleep() {
System.out.println("...zzz...");
}
// Mot clef 'final'.
// Méthode concrète verrouillée.
// Les classes enfants ne peuvent pas la redéfinir.
public final Period getAge(){
return Period.between(LocalDate.now(), birthDate);
}
// Accesseurs & Mutateurs
// ...
}
Nouvelles classes réusinées :
package fr.zelmoacademy.sample;
// Mot clef 'exetends' pour spécifier le nom de la classe parente.
public class Cat extends Pet {
public Cat() {
}
// Annotation 'Override'
// Implémente le comportement spécifique à cette classe.
@Override
public void talk() {
System.out.println("Meow");
}
// Annotation 'Override'
// Implémente le comportement spécifique à cette classe.
// Ne tient pas en compte le comportement de la classe parente.
@Override
public void sleep() {
System.out.println("Purrr");
}
// Méthode spécifique à cette classe.
public void huntMice() {
// ...
}
}
package fr.zelmoacademy.sample;
// Mot clef 'exetends' pour spécifier le nom de la classe parente.
// Mot clef 'final', cette classe ne peut plus être héritée.
public final class Dog extends Pet {
public Dog() {
}
// Annotation 'Override'
// Implémente le comportement spécifique à cette classe.
@Override
public void talk() {
System.out.println("Woof");
}
// Annotation 'Override'
// Implémente le comportement spécifique à cette classe.
// Ne tient pas en compte le comportement de la classe parente.
@Override
public void sleep() {
// Invocation de la méthode parente, tel qu'elle a été défini par cette dernière.
super.sleep();
// Implémentation spécifique de cette classe.
System.out.println("Zzz");
}
// Méthode spécifique à cette classe.
public void sniff() {
// ...
}
}