Problématique : en développement, on fait des traitements identiques pour certaines données communes et des traitements spécifiques pour un certain nombre de données spécifiques.
On s’oriente alors trop souvent vers deux types de solutions qui vont bafouer les principes de base de la programmation.
Vous avez découvert avec les SGBDs, UML et bientôt avec le MCD de Merise que chaque donnée élémentaire est gérée dans son Entité, au bon et à un seul endroit et tous les SGBDs sont capables de retrouver les liens entre les entités.
On évite ainsi les redondances !
Et si on pouvait appliquer le même principe aux traitements ?
Données + Traitements => Classes d’objets
Vous savez qu’un objet possède des :
La classification des connaissances humaines permet une économie de mémoire en permettant d’identifier rapidement les caractéristiques d’un objet appartenant à une classe.
Dans l’univers de la programmation Objet, on appelle cela l’Héritage
Malheureusement, il n’y a pas d’argent à rammasser !
Prenons l’exemple d’une application de santé publique, représentons les objets Individu et Patient :
Code Java correspondant (sans les constructeurs pour le moment):
class Individu { private String nom ; private String prenom ; public String getNom() { return nom ; } public String getPrenom() { return prenom ; } } class Patient extends Individu { private String numeroSecu; public String getNumeroSecu() { return numeroSecu; } }
Toutes les classes sont rattachées directement ou indirectement à la classe Object (java.lang.Object) de Java. Comme nous l’avons vu plus haut, les classes sont organisées de manière hiérarchique. Toutes les classes ont un ancêtre et un seul.
java.lang.Object <- Simplonien <- Apprenant
Ici, la classe Apprenant hérite de la classe Simplonien qui elle-même hérite comme tous les objets Java, de la classe OBJECT du paquetage Java.lang.Object.
Les termes utilisés pour exprimer le principe de l’héritage sont multiples
Dans notre exemple, la classe Simplonien est dite :
La classe Apprenant est dite :
Toutes les classes (sauf la classe Object) ont UN ANCETRE ET UN SEUL.
Pour écrire que la classe Apprenant hérite de la classe Simplonien, on utilise le mot-clé :
EXTENDS qui signifie étendre.
Remarque : Nous n’avons pas besoin de préciser que la classe Simplonien est dérivée de la classe Object du langage Java, ceci est implicite.
Object est l’ancêtre commun (Root) de toutes les classes Java. Il propose des méthodes de base pour tous les comportements généraux comme par exemple :
Ces méthodes de base peuvent être utilisées directement sur tous les objets Java. On peut bien entendu les redéfinir !
Un objet issu d’une classe dérivée est composé de 2 parties :
Toutes les méthodes publiques de la classe ancêtre sont « héritées », donc utilisables à partir de la classe dérivée.
Dans un langage OO (Orienté Objet), c’est l’héritage des méthodes qui va considérablement augmenter le taux de réutilisation du code. Les constructeurs ne font pas partie de l’héritage.
Illustration
class Developpeur extends Simplonien { private String langage ; // constructeur 1 public Developpeur(String nom , String langage ) { super( nom ); this.langage = langage; } // constructeur 2 public Developpeur( String nom ) { this( nom , null ) ; } }
Obligatoirement dans un CONSTRUCTEUR TOUJOURS en PREMIERE INSTRUCTION UNE SEULE FOIS par constructeur
Règle : Les constructeurs ne font pas partie de l’héritage.
Oulala ! Je comprends plus rien à l’objet, et l’ancêtre alors !
Si la classe ancêtre ne possède pas de constructeur sans argument, le constructeur de la classe dérivée doit appeler le constructeur de son ancêtre.
A la création d’un objet d’une classe dérivée on peut écrire un constructeur pour initaliser les données spécifiques à la classe dérivée.
Comment faire pour construire la partie ancêtre de la classe dérivée ?
Solution : Dans le constructeur de la classe dérivée, il suffit d’appeler par le mot clé super() un constructeur de la super-classe en respectant la signature du constructeur.
Remarque : l’appel à super() doit être la première instruction du constructeur.
class Individu { private String nom ; private String prenom ; public Individu(String nom, String prenom) // constructeur { this.nom=nom; this.prenom=prenom; } } class Patient extends Individu { private String numeroSecu; public Patient(String nom, String prenom,String numero) { super(nom, prenom); this.numeroSecu=numero; } }
Quelques règles sur les constructeurs d’une même classe Une classe peut avoir plusieurs constructeurs Un constructeur peut faire appel à un autre de la même classe en utilisant this() ce qui évite de dupliquer du code !
Quelques règles sur les constructeurs d’une même classe
Réécriture d’une méthode (Override)
Une méthode contenue dans une classe ancêtre peut être réécrite (on dit aussi redéfinie) dans une classe dérivée. La méthode de la classe dérivée doit avoir la même signature que celle de la classe ancêtre, elle va surcharger (override) cette méthode ancêtre.
C’est toujours la méthode la plus interne qui sera exécutée par défaut.
Par exemple si on a défini une méthode print() pour la classe Individu, on pourra écrire un print() spécifique pour la classe Patient.
Mais dans le print() de Patient on pourra aussi utiliser grâce à super(), le print() d’Individu
Exemple de réécriture de méthode :
class Individu { private String nom ; private String prenom ; public void print() { ... // formate et imprime nom et prénom } ... } class Patient extends Individu { private String numeroSecu; public void print() { super.print() // appel du print() de la super classe ... // impression spécique à la classe dérivée } }
Les règles dans l’utilisation du mot-clé super sont les mêmes que pour le mot this()
La référence super() permet d’appeler le constructeur de l’ancêtre. Le constructeur de la classe dérivée doit fournir les valeurs nécessaires au constructeur de l’ancêtre comme dans l’exemple ci-dessous :
public Programmeur( String unNom ) { super( unNom ) ; // Appel du constructeur de l'ancêtre }
Exemple avec l’utilisation de super :
public Simplonien( String nom) { ... } public Programmeur( String nom) { super(nom) ; ... }
C’est la capacité d’envoyer un même message à des objets de classes différentes, mais d’activer en fait une méthode spécifique à chaque classe.
Le polymorphisme est intéressant dans un contexte d’héritage.
Ainsi, le même message peut être envoyé à divers objets de la hiérarchie mais il activera la méthode appropriée en fonction de la nature de l’objet récepteur.
Exemple : Si on reprend notre exemple de la méthode print(), imaginons que nous stockions des objets Individu dans une **ArrayList** : des Patients, des Developpeur, Formateur.
On activera la méthode print() de la même façon sur tous les éléments de l’ArrayList sans se soucier de leur spécificité.
Il permet de comparer une instance d’objet à une classe Java. Il renvoie vrai si le nom de la classe fait partie des classes dont l’objet référencé est issu.
Patient patient1 = new Patient(); if (patient1 instanceof Patient) // Vrai if (patient1 instanceof Individu) // Vrai aussi Individu ind = new Individu() if (ind instanceof Individu) // Vrai if (ind instanceof Patient) // Faux
Utiliser l’héritage quand il y a vraie filiation, spécialisation : un patient est un cas particulier d’individu, c’est normal et souhaitable qu’il hérite de toutes ses méthodes.
Utiliser l’agrégation quand on a une relation de contenu à contenant. Une chaudière est un composant d’une maison, mais son comportement est très spécifique.
Exemple :
L’héritage est la base de la réutilisation en Orienté Objet Les classes dérivées permettent d’étendre (extends) les fonctionnalités d’une classe mère. Un classe dérivée hérite de toutes les méthodes publiques des classes ancêtres. Toute classe en Java est dérivée de la classe Object. Le polymorphisme permet d’utiliser un même nom de méthode sans préciser la nature de l’objet récepteur. L’héritage et l’agrégation répondent à des cas de figure différents et sont complémentaires.
Maintenant que vous avez découvert les notions d’héritage avec les diagrammes de classes UML et ce cours, voici un TP pour mettre en application vos acquis avec java.
Pour illustrer cette notion particulière à l’univers Objet voici ce que vous allez écrire :
Créer 2 classes (la troisième est donnée plus bas) :
bonjour() : affiche “Bonjour, tu es prenom et tu as age ans. ET
Voici la classe TestHeritage.java :
package heritage; import heritage.Apprenant.Os; /** * @author Philippe * */ public class TestHeritage { /** * @param args */ public static void main(String[] args) { Apprenant phil = new Apprenant("Philippe",54,"Montreuil", Os.WINDOWS); phil.bonjour(); phil.travailler(); phil.manger("banane"); phil.chomer(); phil.dormir(); Personne jojo = new Personne("Josselin",34); jojo.bonjour(); jojo.manger("pizza"); jojo.dormir(); Personne anissa = new Apprenant("Anissa",23, "Montreuil",Os.MAC); anissa.bonjour(); anissa.manger("chocolat"); anissa.dormir(); } }
par ici