Le Héritage

Une sous-classe (subclass) est une class qui extend une autre classe.
Object
est la class dont tous autres objects sont dérivés.
Héritage est une mécanisme de transfère des propriétés d'une super-classe vers une sous-classe. Un objet d'une sous-classe hérite les propriétés de la super-classe (sauf les constructeurs).
Différence entre héritage et utilisation: Avec l'héritage (mais pas avec le simple utilisation) on a accès aux éléments de la super-classe à partir d'une sous-classe.
On distingue deux cas.
modificateur (dans la super-classe) |
||||
(Accès) Où est la classe? |
public |
protected |
(par défaut) |
private |
(Sous-Classe) Dans le même package |
Oui |
Oui |
Oui |
Non |
(Sous-classe) Dans différents packages |
Oui |
Oui |
Non |
Non |
(Dehors) Même package |
Oui |
Oui |
Oui |
Non |
(Dehors) Différents packages |
Oui |
Non |
Non |
Non |
L'accès à partir d'une sous-classe correspond au cas où on veut accéder aux éléments de la super-classe dans la déclaration d'une sous-classe. Par exemple:
class C1 { int a; protected int b; private int c; } class C2 extends C1 { int d; C2() { a=0; b=0; // c=0; est interdit d=0; } }
Dans la déclaration de C2, on essaie d'accéder aux attributs a, b, et c de la super-classe. Cela correspond aux cas du premier tableau (dans une sous-classe).
Pour un autre exemple:
class C3 extends C1 { int d; C1 c_1; C3() { c_1 = new C1(); c_1.a = 0; c_1.b = 0; // c_1.c = 0; est interdit. } }
Les accès aux attributs a, b et c à partir de c_1 correspond aux cas d'accès en dehors d'une sous-classe parce qu'on essaie de le faire à partir d'une instance "utilisée" dans cette classe (même si on est encore dans la déclaration de la sous-classe C3).
La bonne façon de programmation O-O: On déclare autant que possible les variables locales comme private. Cela a pour effet de protéger ces variables des modifications et des accès non contrôlés de l'extérieur. Afin de permettre à un accès "contrôlé", on crée des méthodes d'accès, par exemple, get… et set….
Exemple:
class Student { private String name; private int num_courses; public Student(...) {...} public void info() {...} public String getName() (return name; } public void setName(String a_name) {name = a_name; } public int getNumCourses() {return num_courses;} public void setNumCourses(int classes) {num_courses = classes;} ... }
Il est alors possible d'ajouter certain contrôle sur les accès aux attributs. Par exemple, il serait possible d'interdire setNumCourses(6) si le règlement n'autorise pas un étudiant d'inscrire en même temps à 6 cours…
Constructeur
- Un constructeur possède le même nom que la classe;
- Une classe peut contenir plus d'un constructeur, avec des paramètres différents;
- Si aucun constructeur est déclaré dans une classe, le constructeur par défaut est automatiquement ajouté, ce qui correspond à:
<nom_classe>() {}
- Si un autre constructeur est déclaré, alors le constructeur par défaut n'est plus ajouté automatiquement.
Overloading / Overriding
Overloading réfère au phénomène que plusieurs version d'une méthode co-existent, mais ces méthodes ont des signatures différentes. La signature est une identification de méthode interne. Elle est crée; en fonction du nom de la méthode et les paramètres. Ainsi, il n'y a pas de conflit entre des méthodes de même nom avec des signatures différentes.
Un exemple de Overloading est la déclaration de plusieurs constructeurs dans une classe, mais avec des paramètres différents.
Overriding réfère au phénomène qu'une nouvelle version d'une méthode avec la même signature a été redéfinie dans une sous-classe. Par exemple:
class C1 { public int a; public C1(int x) { a = x; } public void print() { System.out.println("Version1" + a); } } class C2 extends C1 { public int b; public C2 (int x, int y) { super(x); b = y; } public void print() { System.out.println("Version2" + b); } }
Si on les utilise comme suit:
... main(...) { C1 c_1 = new C1(1); C2 c_2 = new C2(2, 3); c_1.print(); // donne "Version 1 1" c_2.print(); // donne "Version 2 3" ... }
Pour l'instance référée par c_2, la seule version de print() disponible est celle déclarée dans C2. L'ancienne version de C1 a été remplacée (overridden).
Polymorphisme
Cela réfère au phénomène que plusieurs déclarations (versions) des attributs co-existent dans un même objet. L'attribut utilisé dépend comment l'évocation est faite.
Par exemple:
class C1 { public int a; public C1(int x) { a = x; } } class C2 extends C1 { public int a; public int b; public C2 (int x, int y) { super(0); a = x; b = y; } }
Il y a une redéfinition de "int a" dans la classe C2. Voyons comment ces deux variables "a" peuvent être réféées.
C1 c_1; C2 c_2; c_1 = new C1(1); // c_1.a -> 1 c_2 = new C2(2,3); //c_2.a -> 2 c_1 = new C2(2,3); // c_1.a -> 0, la valeur 0 est mis dans a de classe C1 par super(0); // c_2 = new C1(1); est interdit.En réalité, une instance de C2 correspond à une structure de données comme suit:
qui contient deux déclarations de "a". Ces deux "a" correspondent à des places mémoire différentes. La "version" de "a" accédée est déterminée par la classe de la référence. Si on accède à partir d'une référence de la classe C1, alors on accède à la version déclarée dans C1 (c'est le cas de c_1.a). Si l'accès est fait à partir d'une référence de classe C2, alors on accède à la version de C2.
Casting
Étant donné que les deux versions co-existent, on peut donc manipuler les références pour les accéder. Il y a encore la façon de "casting" qui permet de modifier la classe d'une référence comme (C2) c_1 dans l'exemple qui suit:
c_1 = new C2(2,3); // c_1.a donne 0; Mais ((C2) c_1).a donne 2;
De même, on peut aussi faire l'affectation suivant (qui est interdit sans le casting:
c_2 = (C2) c_1; // cela est possible via casting. Mais une autre condition est que l'objet référé par c_1 soit réellement de la classe C2.
La condition pour "caster" une référence à une autre classe (qui peut être une sous-classe ou super-classe de la référence) est que l'objet référé soit de cette classe.
Une comparaison avec Overriding: Dans le cas de méthode, on a seulement une version de méthode disponible, contrairement au cas des attributs. La différence vient du fait que les méthodes sont implicitement "static", donc une seule copie par classe. Quand une sous-classe redéfinit une méthode, l'ancienne méthode n'existe plus (physiquement) pour les objets de cette sous-classe. Tandis que chaque objet possède sa propre copie des attributs non statiques.
Donc, dans l'exemple utilisé pour illustrer Overriding, même si on appelle ((C1) c_2).print() en utilisant du casting, la méthode est toujours celle définie dans C2.
This et super.
Ces deux "références" sont utilisées dans la déclaration des classes/sous-classes. "this" réfère à l'objet courant. Super réfère à la classe supérieure.
Exemple:
class date { int year; void setYear (int year) { this.year = year; } }
Dans la déclaration d'une classe, on peut avoir besoin de référer à
des attributs/méthodes de cette classe. Comme il n'y a pas encore de
référence créée, il est impossible d'utiliser
<référence>.<attribut> pour cela. Mais on peut utiliser
"this" pour référer à l'objet courant. Ainsi, this.
"super" peut être utilisé dans la déclaration d'une sous-classe de deux manières:
- Dans un constructeur de sous-classe, super(…) appelle au constructeur de la super-classe. Contrainte: si on utiliser super(…), cela doit être la première instruction dans le constructeur.
- On peut utiliser super.<attribut/méthode> pour référer à un attribut ou une méthode de la super-classe.
Exemple:
class C1 { String nom; print() { System.out.println(nom);} } class C2 extends C1 { String prenom; print() { super.print(); // appel à print() de C1 System.out.println(prenom); }
javadoc
C'est utile d'utilser le program javadoc pour documenter votre code./** *Class Mammal *Written by Douglas Eck */ public class Mammal extends Animal { public String hab = "Mammals live on land"; public String toString() { return "I am a mammal and " + super.toString(); } /** * Mammals nurse their young * @param amount how much food to give * @param numInfants how many children * @return status successful or not */ public boolean nurseYonng(double amount, int numInfants) { boolean status=true; return status; //seulement pour voir javadoc } } /** *Class Animal *Written by Douglas Eck */ public class Animal { String hab="Animals live on earth"; public String toString() { return "I am an animal."; } public static void main(String[] args) { Cat c = new Cat(); Mammal m=(Mammal)c; System.out.println("c.getHabitat = " + c.getHabitat()); System.out.println("c.hab = " + c.hab); System.out.println("m.getHabitat = " + m.getHabitat()); System.out.println("m.hab = " + m.hab); } public String getHabitat() { return hab; } }javadoc crée beaucoup des affiches d'HTML (aussi index.html). Donc c'est mieux de utiliser un directory seulement pour les affiches:
mkdir docs
cd docs
javadoc -d docs Animal.java Mammal.java Dog.java Cat.java
crée des affiches d'HTML (avec index.html on peut voir tous): docs/index.html
Object
Method Summary | |
protected Object |
clone()
Creates and returns a copy of this object. |
boolean |
equals(Object obj)
Indicates whether some other object is "equal to" this one. |
protected void |
finalize()
Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. |
Class |
getClass()
Returns the runtime class of an object. |
int |
hashCode()
Returns a hash code value for the object. |
void |
notify()
Wakes up a single thread that is waiting on this object's monitor. |
void |
notifyAll()
Wakes up all threads that are waiting on this object's monitor. |
String |
toString()
Returns a string representation of the object. |
void |
wait()
Causes current thread to wait until another thread invokes the notify() method or the
notifyAll() method for this object. |
void |
wait(long timeout)
Causes current thread to wait until either another thread invokes the notify() method or the
notifyAll() method for this object, or a
specified amount of time has elapsed. |
void |
wait(long timeout,
int nanos)
Causes current thread to wait until another thread invokes the notify() method or the
notifyAll() method for this object, or
some other thread interrupts the current thread, or a certain
amount of real time has elapsed. |
Les Exemples
Shapes (un exemple très simple d'héritage) Animaux (héritage et javadoc)Exemple de Coin (
equals(), hashCode(), getClass()
)
Exemple de CloneClass (clone(), ListArray, ListIterator
)
SmartGuys (exemple d'héritage)
ShapesApplet (héritage, animation)
Flock (héritage, animation, démo)
Retour à la page principale d'ift1020Last modified: Tue Sep 21 15:14:08 EDT 2004 par Douglas Eck [douglas D0T eck AT umontreal D0T ca]