précédent | suivant | table des matières s'évaluer

Héritage

Sommaire
  1. Définitions
  2. Constructeurs et héritage
  3. this et super
  4. Opérateur instanceof
  5. Redéfinition des méthodes
  6. Classe abstraite
  7. Exemples
    1. Employés
    2. Formes
  8. Construction d'une instance

1 Définitions.

Une classe Java dérive toujours d’un autre classe, Object quand rien n’est spécifié. Pour spécifier de quelle classe hérite une classe on utilise le mot-clé extends : 

class D extends B {
   . . .
}

La classe D dérive de la classe B. On dit que le classe B est la super classe, la classe de base, ou la classe mère de la classe dérivée D, et que D dérive de B, ou que D est une sous-classe de B.

spécialisation/généralisation

La visibilité protected rend l'accès possible : 

Exemple : 

class B{
   private int a;

protected int b;

int c;

public int d;

} class D extends B {

int x;

void f(){

b = …; } }
On peut alors représenter un objet de la classe D de la façon suivante : représentation mémoire

Une référence à une classe de base peut être affectée d’une référence à une classe dérivée, l’inverse doit faire l’objet d’une opération de conversion de type ou cast : 

B b = new B();
D d = new D();
b = d;
d = b; interdit
d = (D) b ; // OK avec cast

Cette dernière instruction peut lever une exception ClassCastException si le cast est impossible.

2 Constructeurs et héritage.

class X{
   int x;
   
   public X (int x){        
      this.x = x;
   }
}
class Y extends X{

}
provoque l'erreur de compilation : le super constructeur implicite X() n'est pas défini pour le constructeur par défaut.

3 this et super.

Chaque instance est munie de deux références particulières : 

4 Opérateur instanceof.

L’opérateur instanceof permet de savoir à quelle classe appartient une instance : 

class B{ …}

class D extends B{…}

class C {…}

B b = new B();
D d = new D();
C c = new C();

b instanceof B // true
b instanceof D // false
d instanceof B // true
d instanceof D // true

b = d;

b instanceof B // true
b instanceof D // true
c instanceof B // erreur de compilation Erreur No. 365 :
               // impossible de comparer C avec B  en ligne …, colonne ..

Exemple  la méthode equals(Object o), pour une classe X, est définie, en général, de la façon suivante :

public boolean equals(Object o){
   if(this==o) return true;
   if(!(o instanceof X) return false;
   X x = (X)o;
   ...
}

5 Redéfinition des méthodes

Une méthode définie dans une classe peut être redéfinie dans une classe dérivée.

Sans redéfinition de la méthode m Avec redéfinition de la méthode m
  class B {
     void m(){
        System.out.println(
           "méthode m de B");
     }
  }
  class D extends B{
  }
  class B {
     void m(){
        System.out.println(
           "méthode m de B");
     }
  }

  class D extends B{
     void m(){
        System.out.println(
           "méthode m de D");
     }
  }
 B b=new B();
 D d=new  D();
 b.m();
 b = d;
 b.m();


 B b=new B();
 D d=new D();
 b.m();
 b=d; b.m();



Remarques :

  1. Ne pas confondre la redéfinition avec la surcharge : 
  2. Une classe définie avec le modificateur d’accès final ne peut pas être dérivée.
  3. Une méthode définie avec le modificateur d’accès final ne peut pas être redéfinie dans les classes dérivées. Ceci peut se faire pour des raisons : 

6 Classe abstraite

Une classe définie avec le modificateur abstract est une classe abstraite qui ne peut pas produire d’instance. Sa définition peux contenir des méthodes abstraites. En revanche une classe qui contient une méthode abstraite doit être abstraite. Une méthode abstraite est une méthode définie uniquement par son prototype. Le rôle des classes abstraites est la factorisation de fonctionnalités communes à plusieurs classes dérivées.

Exemple : Supposons que nous voulions manipuler deux formes géométriques, rectangle (donné par son point haut, gauche, sa largeur et sa longueur) et cercle (donné par son centre et son rayon). Nous désirons pouvoir déplacer ces formes géométriques, calculer leur surface et leur périmètre .

sans classe abstraite avec classe abstraite : on peut factoiser deplaceDe
 class Rectangle{ 
    int x, y, largeur, longueur;
    Rectangle(int x, int y, int la, int lo){
       this.x = x; this.y = y; 
       largeur = la; longueur = lo;
    }
    void deplaceDe( int dx, int dy){
       x+=dx; y+=dy;
    }
    double perimetre(){
       return 2*(largeur+longueur);
    }
    double surface(){
       return largeur*longueur;
    }
 } 
  
 class Cercle{ 
    int x, y, rayon;
    Cercle(int x, int y, int r){
       this.x = x; this.y = y; 
       rayon = r;
    }
    void deplaceDe( int dx, int dy){
       x+=dx; y+=dy;
    }
    double perimetre(){
       return 2*Math.PI*rayon;
    }
    double surface(){
       return Math.PI*rayon*rayon;
    }
  }
 abstract class Forme{
    int x, y;
    Forme(int x, int y){
       this.x = x; this.y = y;
    }
    void deplaceDe( int dx, int dy){ 
      x+=dx; y+=dy;
    }
    abstract double perimetre();
    abstract double surface();
 } 
 
 class Rectangle extends Forme{
    int largeur, longueur;
    Rectangle(int x, int y, int la, int lo){
       super(x, y);
       largeur = la; longueur = lo;
    }
    double perimetre(){
      return 2*(largeur+longueur);
    }
    double surface(){
      return largeur*longueur;
    }
 }
   
 class Cercle extends Forme{
    int rayon;
    Cercle(int x, int y, int r){
        super(x, y);
        rayon = r;
    }
    double perimetre(){
       return 2*Math.PI*rayon;
    }
    double surface(){
       returnMath.PI*rayon*rayon;
    }
 }

7 Exemples

7.1 Employé

Supposons que nous définissions la classe suivante : 

public class Employe{
   protected String nom ;
   protected double salaire ;
   public Employe ( String nom, double salaire){ 
      this.nom = nom ; this.salaire = salaire ;
   }
   public String getNom(){ return nom ;}
   public double getSalaire() { return salaire ;}
   publicString toString() { return getNom()+” ”+getSalaire(); }
}

Un chef «est un» employé qui a une prime en plus du salaire : 

public class Chef extends Employe{
   private double prime;
   public Chef ( String nom, double salaire, double prime){
      super( nom, salaire) ;
      this.prime = prime;
   }
   public double getSalaire() {return salaire + prime;}
}

La méthode getSalaire pourrait être (mieux ?) programmée de la façon suivante : cela permettrait de changer les accès de nom et salaire en private

public double getSalaire() { return super.getSalaire() + prime;}

Une entreprise est un tableau d’employés : 

Employe entreprise[] = new Employe[5] ;
entreprise[0]= new Chef("Cronos", 1000, 500) ;
entreprise[1]= new Chef("Zeus ", 1000, 600) ;
entreprise[2]= new Employe("Ares", 620) ;
entreprise[3]= new Employe ("Apollon", 700) ;
entreprise[4]= new Employe ("Aphrodite ", 100) ;

L’affichage des noms et salaires des différents employés se fera de la façon suivante : 

for ( int i = 0 ; i< 5 ; ++i){
   if (entreprise[i] instanceof Chef) System.out.print("Chef : ");
   System.out.println( entreprise[i].toString()); 
}

Quand on fait un appel de toString pour une instance de la classe Chef, le code de la méthode toString héritée de Employe s’exécute avec des appels de getNom et de getSalaire qui se font pour un Chef et ce sont les méthodes getNom et getSalaire de la classe Chef qui sont appelées.

Le calcul de la somme des salaires des employés qui ne sont pas des chefs :

double sommeSalaires = 0;
for ( int i = 0 ; I < 5 ; ++i)
   if(!(entreprise[i] instanceof Chef))  sommeSalaires +=  entreprise[i].getSalaire();

7.2 Des formes.

Nous nous proposons de définir une hiérarchie de classes permettant la visualisation de formes sur un écran.
Les classes FormeA, Forme et BiPoints sont des classes abstraites.
diagramme des classes Formes
abstract public class FormeA {
   protected FormeA(){}
   abstract public void affiche(Graphics g);
   abstract public void cache(Graphics g);
   abstract public void deplaceDe(int dx, int dy, Graphics g);
}

abstract public class Forme extends FormeA{ protected int x, y; protectedColor couleur; protected Forme(int x, int y, Color c){ this.x = x; this.y = y; couleur = c; } public void deplaceDe(int dx, int dy, Graphics g){ cache(g); x+=dx; y += dy; affiche(g); } }

La classe Point est une classe concrète pour laquelle nous définissons les fonctionnalités cache, affiche et deplaceDe : 

public class Point extends Forme {
   public Point(int int y, Color c) {
      super(x, y, c);
   }
   public void affiche(Graphics g) {
      g.setColor(couleur);
      g.drawRect(x, y, 1, 1);
   }
   publicvoid cache( Graphics g) {
      g.setColor(Color.white);
      g.drawRect(x, y, 1, 1);
   }
}

La classe Cercle est une classe concrète pour laquelle nous définissons les fonctionnalités cache, affiche : 

public class Cercle extends Forme {
   int rayon;
   public Cercle(int x, int y, int r, Color c) {
      super( x, y, c);
      rayon = r;
   }
    public void affiche(Graphics g) {
      g.setColor(couleur);
      g.drawOval(x-rayon, y-rayon, 2*rayon, 2*rayon);
   }
   public void cache( Graphics g) {
      g.setColor(Color.white);
      g.drawOval(x-rayon, y-rayon, 2*rayon, 2*rayon);
   }
}

La classe abstraite BiPoint met en facteur le constructeur et la méthode deplaceDe de Rectangle et Segment :

abstract public class BiPoint extends Forme {
   int x1, y1;
   public BiPoint( int x, int y, int x1, int y1, Color c) {
      super(x, y, c);
      this.x1 = x1; this.y1 = y1;
   }
   public void deplaceDe(int dx, int dy, Graphics g) {
      cache(g);
      x+=dx; y += dy;x1+=dx; y1+=dy;
      affiche(g);
   } 
}

Les classes Rectangle et Segment se différentient par le tracé :

public class Rectangle extends BiPoint {
   public Rectangle(int x, int y, int x1, int y1, Color c){
      super(x, y, x1, y1, c);<
   }
   public void affiche(Graphics g) {
      g.setColor(couleur);
      g.drawRect(x, y, x1-x, y1-y);
   }

public void cache(Graphics g) { g.setColor(Color.white); g.drawRect(x, y, x1-x, y1-y); } } publicclass Segment extends BiPoint{ public Segment (int x, int y, int x1, int y1, Color c){ super(x, y, x1, y1, c); } public void affiche(Graphics g) { g.setColor(couleur); g.drawLine(x, y, x1-x, y1-y); } public void cache(Graphics g) { g.setColor(Color.white); g. drawLine (x, y, x1-x, y1-y); } }

8 Processus de construction d’une instance

  1. Chargement de la classe si elle n’est pas déjà chargée, avec :
  2. Allocation de la mémoire.
  3. Validation du polymorphisme : pour toute méthode appelée par la suite il sera tenu compte du polymorphisme.
  4. Appel du constructeur de la super classe
  5. Initialisation des attributs ( définis avec une initialisation )
  6. Exécution du corps du constructeur.

Exemple soient les 3 classes suivantes : 

public class Base {
   static{
      System.out.println("bloc statique Base ");
   }
   public Base() {
      System.out.println("constructeur de Base ");
   }
}
public class Derivee extends Base {<
   static {
      System.out.println("bloc statique 1 de Derivee ");
   }
   static UneAutre x1 = new UneAutre(1);
   UneAutre x2 = new UneAutre(2);
   static {
      System.out.println("bloc statique 2 de Derivee ");
   }
   public Derivee() {
      System.out.println("constructeur de Derivee ");
   }
}
public class UneAutre {
   static {
      System.out.println("statique de UneAutre ");
   }
   public UneAutre(int i) {
      System.out.println("constructeur de UneAutre "+ i);
   }
}

La création d’une instance de la classe Derivee produit :

bloc statique de Base
Exécution de 1.a
bloc statique 1 de Derivee 
Exécution de 1.b
statique de UneAutre
Exécution de 1.b
constructeur de UneAutre 1 
Exécution de 1.b
bloc statique 2 de Derivee 
Exécution de 1.b
constructeur de Base
Exécution de 4
constructeur de UneAutre 2 
Exécution de 5
constructeur de Derivee 
Exécution de 6

haut de la page