précédent | suivant | table des matières | s'évaluer |
|
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.
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 : |
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"); } } |
||||
|
|
Remarques :
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. |
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
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 |