ENPC - Programmation - Corrections 6

Exercice 1: la classe Anneau

Cette classe hérite de la classe Disque, et donc de tous ses membres. Ainsi définie, elle hérite donc de toutes ses méthodes publiques, par défaut et protégée. En revanche, comme les champs centre et rayon de la classe Disque ont été déclarés privés, la classe Anneau n'y a pas accès, même si toute instance d'anneau contient ces champs par héritage.

public class Anneau extends Disque {
  private double rayonInterne;
}

Exercice 2: les méthode diametre et diametreInterne

Dans la classe Disque, on ajoute la méthode suivante:

  /*
   * Retourne le diamètre du Disque.
   */
  public double diametre() {
    return 2*rayon;
  }

Dans la classe Anneau, on ajoute la méthode suivante:

  /*
   * Retourne le diamètre de l'Anneau.
   */
  public double diametreInterne() {
    return 2*rayonInterne;
  }  

La méthode diametre de la classe Disque est héritée dans la classe Anneau; on peut donc avoir accès aux deux méthodes dans la classe Anneau, tandis qu'on ne peut accéder qu'à la méthode diametre dans la classe Disque:

  Anneau a = new Anneau();
  a.diametre();               // correct (methode héritée de Disque)
  a.diametreInterne();        // correct
  Disque d = new Disque();
  d.diametre();               // correct
  d.diametreInterne();        // erreur 

Exercices 3 et 4: constructeurs

Si la classe Disque ne contient pas de constructeur sans argument (qu'il soit explicitement ou implicitement défini), alors la classe Anneau ci dessus qui ne contient aucun constructeur explicite ne peut pas compiler. En fait, le compilateur rajoute dans ce cas un constructeur implicite qui fait un appel à super(), c'est à dire le constructeur de la super-classe (i.e., Disque) sans arguments. Si ce derneir n'existe pas, la compilation détecte une erreur. Dans un tel cas, on peut définir explicitement un constructeur sans argument de la classe Anneau qui fait appel au constructeur à deux arguments de la super classe.

  public Anneau() {
    super(0,0);
    this.rayonInterne = 0; 
  }

Néanmoins, puisqu'un constructeur sans argument existe dans notre classe Disque, on peut définir un constructeur d'anneau sans argument qui lui affecte les caractéristiques d'un disque par défaut et dont le rayon interne est nul:

  public Anneau() {
    super();
    this.rayonInterne = 0; 
  }

Remarquez que le code ci-dessus produit exactement le même effet que de n'écrire aucun constructeur dans la classe Anneau.
En revanche, il peut être pratique d'avoir en plus un constructeur avec arguments comme celui-ci:

  public Anneau(Point centre, double rayon, double rayonInterne) {
    super(centre, rayon);
    this.rayonInterne = rayonInterne;
  }

Exercices 5: masquage des champs

Dans la classe Disque, si on définit un champ couleur de type String comme ceci:

public class Disque {
  private Point centre;
  private double rayon;      
  String couleur = "Noir";
  // ...
}

Alors ce champ est visible partour dans la classe Disque, et il est hérité dans la classe Anneau (il y est visible pisqu'on ne l'a pas mis privé dans Disque). Il est donc possible d'écrire:

  Disque d = new Disque();
  System.out.println(d.couleur);  // Affiche Noir
  Anneau a = new Anneau();
  System.out.println(a.couleur);  // Affiche Noir

Si maintenant on définit un champ couleur, de même nom mais de type différent dans la classe Anneau:

public class Anneau extends Disque implements Mesurable {
  private double rayonInterne;
  int couleur = 0;
  // ...
}

alors ce champ masque, dans la classe Anneau, l'existence du champ hérité de la classe Disque. Par exemple, le même code que ci-dessus produit maintenant un autre affichage:

  Disque d = new Disque();
  System.out.println(d.couleur);  // Affiche Noir
  Anneau a = new Anneau();
  System.out.println(a.couleur);  // Affiche 0, car le champ String est
                                  // masqué par le champ int

Maintenant, imaginons que nous ayons une instance de la classe Anneau. Pour récupérer la valeur du champ couleur déclaré dans la super-classe Disque, il y a deux solutions:

Exercices 6: redéfinition des méthodes

La méthode surface() de la classe Disque qui est héritée dans la classe Anneau est incorrecte pour les instances de cette dernière classe. Aussi, nous voudrions redéfinir la méthode surface() dans la classe Anneau:

  /**
   * Retourne la surface de l'anneau.
   */ 
  public double surface() {
    return  Math.PI * rayon * rayon -
      Math.PI * rayonInterne * rayonInterne;
  }

Le problème est qu'ici le champ rayon est privé dans la classe Disque et n'est donc pas accessible depuis la classe Anneau. Nous alons donc utiliser la méthode surface() de Disque pour définir la méthode surface() de Anneau:

  public double surface() {
    return  super.surface() - Math.PI * rayonInterne * rayonInterne;
  }

Ceci montre comment, à partir de l'instance (this) de la classe Anneau (c'est à dire dans la classe), on peut acccéder à la méthode redéfinie de la super classe: grâce au mot-clé super.

En revanche, si on dispose d'une variable a contenant la référence à une instance de la classe Anneau et que l'on est à l'extérieur de la classe Anneau, il n'est pas possible d'accéder via cette référence à la méthode surface() redéfinie de la classe Disque. Voici un exemple qui illustre que, après l'estimation effectuée à la compilation, la machine virtuelle utilise toujours à l'exécution la méthode la plus spécifique possible par rapport au type réel de l'instance sur laquelle la méthode est appellée.

  Disque d = new Disque(p,1);
  System.out.println(d.surface());           // Affiche 3.141592653589793
  Anneau a = new Anneau(p,1,2);
  System.out.println(a.surface());           // Affiche 2.356194490192345 
  Disque v = a;
  System.out.println(v.surface());           // Affiche 2.356194490192345
  System.out.println(((Disque)a).surface()); // Affiche 2.356194490192345

Etienne.Duris[at]univ-mlv.fr - © École Nationale des Ponts et Chaussées - Janvier 2002 - http://www-igm.univ-mlv.fr/~duris/ENPC/