• Aucun résultat trouvé

Exemple d’héritage : la classe PointNommé (incomplète)

Dans le document Héritage en JAVA (Page 47-72)

Remarque : L’héritage évite de faire du copier/coller (toujours dangereux).

Dans la sous-classe, on peut ajouter : – de nouveaux attributs

private String nom;

– de nouvelles méthodes.

public String getNom() { ... }

public void nommer(String nouveauNom) { ... }

Remarque : Ajouter une nouvelle méthode s’entend au sens de la surcharge.

Par exemple, sur le PointNommé on peut ajouter : Enrichissement

Héritage et constructeurs

Principe : Tout constructeur d’une sous-classe doit appeler un des constructeurs de la super-classe.

Justification : Hériter d’une classe, c’est récupérer ses caractéristiques qui doivent donc être initialisées. Dans un PointNommé, il y a un Point et un nom.

En pratique : On utilise super suivi des paramètres effectifs permettant au compilateur de sélectionner le constructeur de la classe parente.

– L’appel à super doit être la première instruction du constructeur ! – La classe parente est toujours initialisée avant la sous-classe.

– Si aucun appel à super n’est fait, le compilateur appelle automatiquement le constructeur par défaut de la classe parente super().

D’où le danger de définir un constructeur par défaut !

Exemple d’utilisation la classe PointNommé :

Remarque : La classe PointNommé a bien hérité de toutes les caractéristiques de Point.

Le polymorphisme consiste à considérer les fonctionnalités suivant le type réel d'un objet et non pas suivant le type de la variable dans laquelle il est stocké.

Le polymorphisme peut être vu comme la capacité de choisir dynamiquement la méthode qui correspond au type réel de l’objet.

Le sous-typage permet de stocker un objet comme une variable d'un super-type, le polymorphisme fait en sorte que les méthodes soient appelées en fonction de la classe de l'objet.

Le polymorphisme fait en sorte que certaines parties du programme soient spécialisées en fonction du type réel de l'objet.

3. Polymorphisme

Appel virtuel & compilation:

Le mécanisme d'appel polymorphe (appel virtuel) est décomposé en deux phases : 1) Lors de la compilation, le compilateur choisit la méthode la plus spécifique

en fonction du type déclaré des arguments.

2) Lors de l'exécution, la MV choisit la méthode en fonction de la classe du receveur (l'objet sur lequel on applique '.').

Il n'y a pas d'appel virtuel si la méthode est :

statique (pas de receveur)

private (pas de redéfinition possible, car pas visible)

final ou la classe est final (pas le droit de redéfinir)

Redéfinition de méthodes

- Le fait qu'une méthode redéfinisse une autre dépend:

– Du nom de la méthode

– Des modificateurs de visibilité des méthodes – De la signature des méthodes

– Des exceptions levées par la méthode (throws)

public class A {

public void call() { // implantation 1 }

}

Attention : Il ne faut pas confondre la redéfinition (en anglais

«Overriding») et la surcharge (en anglais «Overloading») qui correspond à la possibilité de définir des comportements différents pour la même

public class B extends A {

@Override public void call() { // implantation 2

} }

L'annotation @Override indique au compilateur de générer une erreur si une méthode ne redéfinit pas une autre.

Si la classe B hérite de la classe A - Classe B "EST-UN" Classe A

- toute méthode m de A peut-être invoquée sur une instance de la classe B (+ transitivité sur l’héritage de A)

Le polymorphisme consiste à exploiter cela en fournissant un B dans les expressions "qui attendent" un A.

Choix de la méthode : liaison dynamique

Si p référence un PointCouleur, quelle méthode toString() va être invoquée

? 1. La méthode toString() est définie dans Point 2. Mais elle est spécialisée dans PointCouleur

Si p référence un PointCouleur, quelle méthode toString() va être invoquée

? 1. La méthode toString() est définie dans Point 2. Mais elle est spécialisée dans PointCouleur

C’est la version la plus spécialisée (PointCouleur.toString()) qui est invoquée car la recherche de la méthode débute dans la classe effective de l’objet référencé par p (approche bottom → top).

La recherche est menée à l’exécution et ce mécanisme est appelé la liaison dynamique.

Lorsqu’une méthode est spécialisée dans une sous-classe, sa visibilité peut être augmentée (ex. protected --> public) mais elle ne peut pas être réduite (ex. public --> private)

On veut coder une classe Zoo qui aura plusieurs cages permettant d’accueillir différents types d’animaux. Il faut donc implémenter une classe Cage permettant de contenir tous ces animaux

Du code polymorphe: le problème

Du code polymorphe: la mauvaise solution

Ici, la surcharge est une très mauvaise solution.

Du code polymorphe: la bonne solution

La bonne solution consiste à utiliser le polymorphisme, en implémentant une méthode accueillir générique pour tout les animaux.

Son paramètre étant de type Animal on pourra l’appeler avec une référence de Lion ou de Singe.

A retenir :

Une sous-classe hérite une méthode d'une super-classe (nom et signature de la méthode identiques), mais dont le comportement est différent (adapté à la sous-classe par redéfinition de la méthode). Aussi appelé spécialisation.

Ex : La classe Velo hérite de la classe Vehicule la méthode demarrer(), mais celle-ci est redéfinie pour être adaptée à la classe Velo (par exemple demarrer() pour la sous-classe Velo inclut pedaler(), etc., alors que demarrer() pour la super-classe Vehicule inclut insererclef()).

Classes abstraites

Définition :

Une classe contenant au moins une méthode abstraite est appelée une classe abstraite et cela doit être explicitement précisé dans la déclaration avec : abstract class.

Remarques :

- Une classe abstraite peut contenir des méthodes concrètes.

- Une classe peut être déclarée abstraite sans contenir de méthodes abstraites.

Interfaces

Quand toutes les méthodes d'une classe sont abstraite et qu'il n'y a aucun attribut, on aboutit à la notion d'interface.

Définition :

- Une interface est un prototype de classe. Elle définit la signature des méthodes qui doivent être implémentées dans les classes construites à partir de ce prototype.

- Une interface est une classe purement abstraite dont toutes les méthodes sont abstraites et publiques. Les mots-clés abstract et public sont optionnels.

Interface Comparable

- Comparer les éléments avec une relation d’ordre.

- Java: la classe des éléments à comparer implémente l'interface Comparable.

- Déjà le cas de Byte, Short, Integer, Long, Float, Double,…

- Une seule méthode: compareTo

- Argument: Object objectToCompareTo - Renvoi: int (résultat de la comparaison)

0 si cette instance et objectToCompareTo sont égales

> 0 si cette instance est supérieure à objectToCompareTo

< 0 si cette instance est inférieure à objectToCompareTo

- Utilisation:

class Employee implements Comparable {…}

- Doit implémenter

public int compareTo(Object compareTo) { // code ici

}

Interface Comparable

Exemple

public class Employee implements Comparable { public Long id;

public String name;

public String phone;

    public Employee(Long i, String n, String p) { id = i;

name = n;

phone = p;

}

// Implement the compareTo() method

    global Integer compareTo(Object compareTo) { Employee compareToEmp = (Employee)compareTo;

if (id == compareToEmp.id) return 0;

if (id > compareToEmp.id) return 1;

return -1;

}

Interface Cloneable

- Il arrive que l'on doive cloner un objet: création d'une copie par valeur de cet objet.

- Java: pour pouvoir être clonée, une classe doit implémenter l'interface Cloneable.

—> la classe peut réécrire la méthode clone() héritée de la classe Object afin que ses instances puissent procéder à une copie de ses attributs vers une nouvelle instance (copie de l'objet par valeur).

- Remarque: l'interface Cloneable ne contient PAS la méthode clone(). Par conséquent, il est impossible de cloner un objet si sa classe ne fait qu'implémenter cette interface.

- Par convention, les classes implémentant l'interface Cloneable doivent réécrire la méthode Object.clone() (qui est protected) avec une visibilité public.

- Une classe réécrivant la méthode clone() doit implémenter l'interface Cloneable sous peine de se retrouver avec une CloneNotSupportedException.

- Pour l’objet « clonable » x, l’expression x.clone() != x renvoie True.

- Aussi l’expression x.clone().getClass() == x.getClass()

public class Patronyme implements Cloneable { private String prenom;

private String nom;

public Patronyme(String prenom, String nom) { this.prenom = prenom;

this.nom = nom;

}

public Object clone() { Object o = null;

try {

// On récupère l'instance à renvoyer par l'appel de la // méthode super.clone()

o = super.clone();

} catch(CloneNotSupportedException cnse) {

// Ne devrait jamais arriver car nous implémentons // l'interface Cloneable

cnse.printStackTrace(System.err);

}

// on renvoie le clone

Exemple

public class Personne implements Cloneable { private Patronyme patronyme;

private int age;

public Personne(Patronyme patronyme, int age) { this.patronyme = patronyme;

this.age = age;

}

public Object clone() {

Personne personne = null;

try {

// On récupère l'instance à renvoyer par l'appel de la // méthode super.clone()

personne = (Personne) super.clone();

} catch(CloneNotSupportedException cnse) {

// Ne devrait jamais arriver car nous implémentons // l'interface Cloneable

cnse.printStackTrace(System.err);

}

// On clone l'attribut de type Patronyme qui n'est pas immuable.

personne.patronyme = (Patronyme) patronyme.clone();

// on renvoie le clone return personne;

} }

public class Jouet implements Cloneable {

} catch(CloneNotSupportedException cnse) {

// Ne devrait jamais arriver car nous implémentons // l'interface Cloneable

cnse.printStackTrace(System.err);

}

// on renvoie le clone return o;

public class Enfant extends Personne { private Jouet jouetPrefere;

public Enfant(Patronyme patronyme, int age, Jouet jouetPrefere) { super(patronyme, age);

this.jouetPrefere = jouetPrefere;

}

public Object clone() {

// On récupère l'instance à renvoyer par l'appel de la // méthode super.clone() (ici : Personne.clone())

Enfant enfant = (Enfant) super.clone();

// On clone l'attribut de type Jouet qui n'est pas immuable.

enfant.jouetPrefere = (Jouet) jouetPrefere.clone();

// on renvoie le clone return enfant;

} }

public class CloneMain {

public static void main(String []args) {

Personne personne1 = new Personne(new Patronyme("Jean", "Dupond"), 30);

Personne personne2 = (Personne) personne1.clone();

System.out.println(personne1);

System.out.println(personne2);

Enfant enfant1 = new Enfant(new Patronyme("Pierre", "Dupond"), 10, new Jouet("Teddy bear"));

Enfant enfant2 = (Enfant) enfant1.clone();

System.out.println(enfant1);

System.out.println(enfant2);

} }

Dans le document Héritage en JAVA (Page 47-72)

Documents relatifs