FMIN 110 GMIN 327 Liaison statique et dynamique

  • Slides: 48
Download presentation
FMIN 110 -GMIN 327 Liaison statique et dynamique Variables et méthodes de classe

FMIN 110 -GMIN 327 Liaison statique et dynamique Variables et méthodes de classe

Liaison dynamique Liaison statique

Liaison dynamique Liaison statique

Liaison dynamique • message Appt a; a = new Appt. Luxe(…); … a. loyer();

Liaison dynamique • message Appt a; a = new Appt. Luxe(…); … a. loyer(); • Type statique (type de la variable), utilisé pour vérifier si loyer() existe sur les Appt • Type dynamique (type de l’objet/de l’instance, derrière le new), utilisé pour choisir (lier) la méthode loyer() la plus spécifique

Liaison dynamique • Pour un message o. m() • une méthode m() est recherchée

Liaison dynamique • Pour un message o. m() • une méthode m() est recherchée en commençant par le type dynamique de o (sa classe) et en remontant dans ses super-classes • la méthode exécutée est celle qui est trouvée dans la classe la plus proche en possédant une

Soient les classes A et B contenant les méthodes : Classe A Classe B

Soient les classes A et B contenant les méthodes : Classe A Classe B String f() {return "f. A "; } String g() {return "g. A "; } String i() {return "i. A "+ this. f(); } String f() {return "f. B " + super. f(); } String h() {return "h. B "+ super. f(); } String k() {return "k. B " + g(); } Qu’affiche le programme suivant : B b = new B(); System. out. println(b. f()); System. out. println(b. g()); System. out. println(b. h()); System. out. println(b. k()); System. out. println(b. i());

Classe A Classe B String f() {return "f. A "; } String g() {return

Classe A Classe B String f() {return "f. A "; } String g() {return "g. A "; } String i() {return "i. A "+ this. f(); } String f() {return "f. B " + super. f(); } String h() {return "h. B "+ super. f(); } String k() {return "k. B " + g(); } B b = new B(); System. out. println(b. f()); f. B f. A • b de type dynamique B • f apparaît dans B • affiche f. B • appelle super. f() pour b • f recherchée à partir de A, elle apparaît dans A • affiche f. A

Classe A Classe B String f() {return "f. A "; } String g() {return

Classe A Classe B String f() {return "f. A "; } String g() {return "g. A "; } String i() {return "i. A "+ this. f(); } String f() {return "f. B " + super. f(); } String h() {return "h. B "+ super. f(); } String k() {return "k. B " + g(); } B b = new B(); System. out. println(b. g()); g. A • b de type dynamique B • g n’apparaît pas dans B – on remonte dans A • g apparaît dans A • affiche g. A

Classe A Classe B String f() {return "f. A "; } String g() {return

Classe A Classe B String f() {return "f. A "; } String g() {return "g. A "; } String i() {return "i. A "+ this. f(); } String f() {return "f. B " + super. f(); } String h() {return "h. B "+ super. f(); } String k() {return "k. B " + g(); } B b = new B(); System. out. println(b. h()); h. B f. A • b de type dynamique B • h apparaît dans B • affiche h. B • appelle super. f() pour b • f recherchée à partir de A, elle apparaît dans A • affiche f. A

Classe A Classe B String f() {return "f. A "; } String g() {return

Classe A Classe B String f() {return "f. A "; } String g() {return "g. A "; } String i() {return "i. A "+ this. f(); } String f() {return "f. B " + super. f(); } String h() {return "h. B "+ super. f(); } String k() {return "k. B " + g(); } B b = new B(); System. out. println(b. k()); k. B g. A • b de type dynamique B • k apparaît dans B • affiche k. B • appelle g() = this. g()) pour b • g recherchée à partir de B, elle apparaît dans la superclasse A • affiche g. A

Classe A Classe B String f() {return "f. A "; } String g() {return

Classe A Classe B String f() {return "f. A "; } String g() {return "g. A "; } String i() {return "i. A "+ this. f(); } String f() {return "f. B " + super. f(); } String h() {return "h. B "+ super. f(); } String k() {return "k. B " + g(); } B b = new B(); System. out. println(b. i()); i. A f. B f. A • b de type dynamique B • i cherchée à partir de B, trouvée dans A • affiche i. A • appelle this. f() pour this = b • f recherchée à partir de B • affiche f. B et appelle super. f() • f recherchée à partir de A, trouvée dans A • affiche f. A

Retour super = accès à la méthode masquée Classe A Classe B String f()

Retour super = accès à la méthode masquée Classe A Classe B String f() {return "f. A"; } String g() {return "g. A"; } String f() {return "f. B " + super. f(); } String h() {return "h. B " + super. f(); } String k() {return "k. B " + super. g(); } Dans h() : super. f() bof - conception ? Dans k() : super. g() NON - écrire g() Règle : on utilise super. f() dans une nouvelle définition de f()

On ne peut faire appel qu'à la méthode masquée Classe A void f() Classe

On ne peut faire appel qu'à la méthode masquée Classe A void f() Classe B void f(){. . . super. f()} Classe C void f() { …. super. f() ; } f() de A

Peut-on "redéfinir" une méthode privée? A B private f() public f() { super. f().

Peut-on "redéfinir" une méthode privée? A B private f() public f() { super. f(). . } super. f() impossible car f() privée en A En fait c'est comme si B définissait une nouvelle méthode

Redéfinition de méthode dans une sous-classe Une redéfinition de méthode se fait - sans

Redéfinition de méthode dans une sous-classe Une redéfinition de méthode se fait - sans changer la liste de types paramètres - le type de retour peut être spécialisé (> Java 1. 5) La protection peut être affaiblie (package -> protégée -> publique) Ce sont des mesures pour garantir la substituabilité syntaxique et la liaison dynamique

surcharge = Liaison statique A B public void f(int a) public void f(String b)

surcharge = Liaison statique A B public void f(int a) public void f(String b) Une instance de B possède 2 méthodes f B b = new B(); b. f(5); //f de A b. f("5"); // f de B

Un exemple de surcharge • Dans la classe Appartement une méthode set. Superficie :

Un exemple de surcharge • Dans la classe Appartement une méthode set. Superficie : public void set. Superficie(double s){superficie=s; } • Dans la classe Appartement. Normal une méthode set. Superficie : public void set. Superficie(int s) { if (s <= 200) super. set. Superficie(s); else System. out. println("Erreur superficie trop grande pour un appartement normal"); }

A l’exécution : public static void main(String[] args) { Appartement a 2 = new

A l’exécution : public static void main(String[] args) { Appartement a 2 = new Appartement. Normal("rue des sapins", 1870, 100, 6, 2); Appartement. Normal a 6 = new Appartement. Normal("rue des sapins", 1870, 100, 6, 2); System. out. println("avec a 2"); a 2. set. Superficie(250); ----------------------------------------affiche : avec a 2 et appelle set. Superficie(double) dans Appartement – set. Superficie(int) ne redéfinit pas set. Superficie (double) – a 2 est de type statique Appartement le compilateur cherche une méthode à laquelle a 2. set. Superficie(250) peut être conforme dans Appartement Il trouve set. Superficie (double) qui n’est pas redéfini

A l’exécution : public static void main(String[] args) { Appartement a 2 = new

A l’exécution : public static void main(String[] args) { Appartement a 2 = new Appartement. Normal("rue des sapins", 1870, 100, 6, 2); Appartement. Normal a 6 = new Appartement. Normal("rue des sapins", 1870, 100, 6, 2); (…) System. out. println("avec a 6"); a 6. set. Superficie(250); } ----------------------------------------affiche : avec a 6 et appelle set. Superficie(int) dans Appartement. Normal affiche : Erreur superficie trop grande pour un appartement normal – a 6 est de type statique Appartement. Normal le compilateur cherche une méthode à laquelle a 6. set. Superficie(250) peut être conforme dans Appartement. Normal Il trouve set. Superficie (int)

Tests de type … Une méthode d’égalité sur les appartements • Dans la classe

Tests de type … Une méthode d’égalité sur les appartements • Dans la classe Appartement : public boolean equals(Appartement autre) { return this. adresse. equals(autre. adresse) && this. annee. Construction==autre. annee. Construction && this. superficie==autre. superficie && this. nb. Pieces==autre. nb. Pieces; }

On aimerait écrire ainsi une méthode d’égalité sur les appartements de luxe • Dans

On aimerait écrire ainsi une méthode d’égalité sur les appartements de luxe • Dans la classe Appartement. Luxe on écrirait equals : public boolean equals(Appartement. Luxe autre) { return super. equals(autre) && this. quartier. equals(autre. quartier); } • PB : Ce n’est pas une redéfinition mais une surcharge • Il faut être vigilant sur le comportement

La méthode main public static void main(String[] args) { Appartement a 1 = new

La méthode main public static void main(String[] args) { Appartement a 1 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Arceaux"); Appartement a 2 = new Appartement. Normal("rue des sapins", 1870, 150, 6, 2); Appartement a 3 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Montferrier"); Appartement. Luxe a 4 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Arceaux"); Appartement. Luxe a 5 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Clapiers"); System. out. println("a 1 égale a 2 : "+ a 1. equals(a 2)); System. out. println("a 1 égale a 3 : "+ a 1. equals(a 3)); System. out. println("a 1 égale a 4 : "+ a 1. equals(a 4)); System. out. println("a 4 égale a 5 : "+ a 4. equals(a 5)); }

Etat des lieux • Les méthodes equals des classes Appartement. Normal et Appartement. De.

Etat des lieux • Les méthodes equals des classes Appartement. Normal et Appartement. De. Luxe n’ont pas la même signature que la méthode equals de la classe Appartement. Ce ne sont pas des redéfinitions mais des surcharges.

public static void main(String[] args) { Appartement a 1 = new Appartement. Luxe("rue des

public static void main(String[] args) { Appartement a 1 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Arceaux"); Appartement a 2 = new Appartement. Normal("rue des sapins", 1870, 150, 6, 2); Appartement a 3 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Montferrier"); Appartement. Luxe a 4 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Arceaux"); Appartement. Luxe a 5 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Clapiers"); System. out. println("a 1 égale a 2 : "+ a 1. equals(a 2)); appelle equals de Appartement et compare les attributs d’appartement TRUE (pourtant les types sont différents) System. out. println("a 1 égale a 3 : "+ a 1. equals(a 3)); appelle equals de Appartement et compare les attributs d’appartement TRUE (pourtant les quartiers sont différents) System. out. println("a 1 égale a 4 : "+ a 1. equals(a 4)); appelle equals de Appartement et compare les attributs d’appartement TRUE (et en effet les quartiers sont semblables) System. out. println("a 4 égale a 5 : "+ a 4. equals(a 5)); appelle equals de Appartement. Luxe et compare tous les attributs FALSE (et en effet les quartiers sont différents) }

La coercition et le test de type • Dans la classe Appartement. Luxe, on

La coercition et le test de type • Dans la classe Appartement. Luxe, on va redéfinir la méthode equals et non la surcharger : Test de type public boolean equals(Appartement autre) { Coercition autre Appartement. Luxe autre. L=null; est de type if (autre instanceof Appartement. Luxe) { statique autre. L = (Appartement. Luxe) autre; Appartement. Luxe return super. equals(autre) && this. quartier. equals(autre. L. quartier); } else return false; }

Ce sont bien des redéfinitions !

Ce sont bien des redéfinitions !

public static void main(String[] args) { Appartement a 1 = new Appartement. Luxe("rue des

public static void main(String[] args) { Appartement a 1 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Arceaux"); Appartement a 2 = new Appartement. Normal("rue des sapins", 1870, 150, 6, 2); Appartement a 3 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Montferrier"); Appartement. Luxe a 4 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Arceaux"); Appartement. Luxe a 5 = new Appartement. Luxe("rue des sapins", 1870, 150, 6, "Clapiers"); System. out. println("a 1 égale a 2 : "+ a 1. equals(a 2)); appelle equals de Appartement. Luxe FALSE (en effet les types sont différents) System. out. println("a 1 égale a 3 : "+ a 1. equals(a 3)); appelle equals de Appartement. Luxe et compare tous les attributs FALSE (en effet les quartiers sont différents) System. out. println("a 1 égale a 4 : "+ a 1. equals(a 4)); appelle equals de Appartement. Luxe et compare tous les attributs TRUE (et en effet les attributs sont semblables, y compris les quartiers) System. out. println("a 4 égale a 5 : "+ a 4. equals(a 5)); appelle equals de Appartement. Luxe et compare tous les attributs FALSE (et en effet les quartiers sont différents) }

La coercition et le test de type • Application : si on recherche des

La coercition et le test de type • Application : si on recherche des appartements dans les appartements gérés par une agence immobilière, il faut utiliser la version de la méthode « equals » avec coercition et test de type. • La coercition de type est ici sans risque car appart. Geres ne contient que des instances des sous-classes de la classe Appartement (et qu’elle est faite sous condition de type). • Règle générale : le moins possible de coercitions et de tests de type. A part le cas présent (multi-méthodes) ils révèlent souvent une mauvaise conception.

Principe de substitution de Liskov • Lorsqu’un type S est sous-type d’un type T

Principe de substitution de Liskov • Lorsqu’un type S est sous-type d’un type T Tout objet de type S peut remplacer un objet de type T sans que le programme n’ait de comportement imprévu ou ne perde de propriétés souhaitées.

Substituabilité La substituabilité syntaxique demande - la contravariance des paramètres - les paramètres varient

Substituabilité La substituabilité syntaxique demande - la contravariance des paramètres - les paramètres varient en sens inverse de la redéfinition. - la covariance du type de retour - le type de retour varie dans le même sens que la redéfinition

Le choix de Java pour la redéfinition de méthode • Invariance des paramètres –

Le choix de Java pour la redéfinition de méthode • Invariance des paramètres – Dans Appartement, Appartement. Luxe et Appartement. Normal, on a la même signature pour equals public boolean equals(Appartement autre); • Covariance des types de retour – Dans Appartement : public abstract Appartement clone(); – Dans Appartement. Luxe : public Appartement. Luxe clone(); – Dans Appartement. Normal : public Appartement. Normal clone();

Substituabilité La substituabilité comportementale demande qu’il n’y ait pas de comportement d’une instance d’une

Substituabilité La substituabilité comportementale demande qu’il n’y ait pas de comportement d’une instance d’une sous-classe inattendu pour une instance de la classe Appartement public void set. Superficie(double s){superficie=s; } Appartement. Normal public void set. Superficie(double s) { if (s <= 200) super. set. Superficie(s); else System. out. println("Erreur superficie trop grande"); } … main … { Appartement a = new Appartement. Normal("rue des sapins", 1870, 100, 6, 2); a. set. Superficie(250); } L’erreur est inattendue pour un appartement

Constantes • Notion de constante – attribut ou paramètre ou variable locale – initialisé

Constantes • Notion de constante – attribut ou paramètre ou variable locale – initialisé une seule fois – introduit par final

Constante (attribut) public class Personne { private String nom; private final Personne mère; private

Constante (attribut) public class Personne { private String nom; private final Personne mère; private final int année. Naissance; … }

Constante public class Personne { private String nom; private final Personne mère; private final

Constante public class Personne { private String nom; private final Personne mère; private final int année. Naissance; public Personne() { } Ne peut être écrit si on l'appelle, il y a un risque mère et année. Naissance ne soient pas initialisées

Constante public class Personne { private String nom; private final Personne mère; private final

Constante public class Personne { private String nom; private final Personne mère; private final int année. Naissance; Pour effectuer une unique initialisation (à la création) public Personne(String nom, Personne mère, int année. Naissance) { this. nom = nom; this. mère = mère; this. année. Naissance = année. Naissance; }

Constante public class Personne { private String nom; private final Personne mère; private final

Constante public class Personne { private String nom; private final Personne mère; private final int année. Naissance; public void set. Année. Naissance(int année. Naissance) {this. année. Naissance = année. Naissance; } Ne peut être écrit si on l'appelle, il y a un risque de modifier la valeur de année. Naissance

Constante (paramètre) public (…) affiche. Plus. Petit(int i, int j) { if (i<j) System.

Constante (paramètre) public (…) affiche. Plus. Petit(int i, int j) { if (i<j) System. out. println(i); else System. out. println(j); }

Constante (paramètre) public (…) affiche. Plus. Petit(int i, int j) { Eviter car i,

Constante (paramètre) public (…) affiche. Plus. Petit(int i, int j) { Eviter car i, j passés par valeur if (i<j) j=i; System. out. println(j); } Pour l’empêcher : public (…) affiche. Plus. Petit(final int i, final int j) { ………… }

Variable de classe • Variable globale, partagée par toutes les instances de la classe

Variable de classe • Variable globale, partagée par toutes les instances de la classe (et de ses sous-classes) • introduite par static • Les variables locales peuvent aussi être static, mais nous n’étudierons pas leur utilisation dans ce cours

Variable de classe public abstract class Objet. Postal { abstract public double tarif. Base();

Variable de classe public abstract class Objet. Postal { abstract public double tarif. Base(); } public class Lettre extends Objet. Postal { private static double tarif. Base. Le = 0. 5; public double tarif. Base(){return Lettre. tarif. Base. Le; } } public class Colis extends Objet. Postal { private static double tarif. Base. Co = 2; public double tarif. Base(){return Colis. tarif. Base. Co; } }

Variable de classe public class Lettre extends Objet. Postal { private static double tarif.

Variable de classe public class Lettre extends Objet. Postal { private static double tarif. Base. Le = 0. 5; public double tarif. Base(){return Lettre. tarif. Base. Le; } } • Valeur non enfouie dans le code par opposition à l’écriture avec public double tarif. Base(){return 0. 5; } • Eviter de les initialiser dans un constructeur car elles ne sont pas propres à une instance

Variable de classe public class Lettre extends Objet. Postal { private static double tarif.

Variable de classe public class Lettre extends Objet. Postal { private static double tarif. Base. Le = 0. 5; public double tarif. Base(){return Lettre. tarif. Base. Le; } } public class Colis extends Objet. Postal { private static double tarif. Base. Co = 2; public double tarif. Base(){return Colis. tarif. Base. Co; } } Lettre. tarif. Base. Le 0. 5 Colis. tarif. Base. Co 2

Variable de classe versus d’instance Lettre l 1 = new Lettre("montpellier", "kouk, igloo 2,

Variable de classe versus d’instance Lettre l 1 = new Lettre("montpellier", "kouk, igloo 2, banquise nord", "345", 0. 2, 0. 005, 1, Type. Urgence. ordinaire); Lettre l 2 = new Lettre( "nîmes", "igloo 17, banquise sud", "7775", 0. 4, 0. 007, 1, Type. Urgence. urgente); l 2 l 1 "montpellier" "kouk, igloo 2, banquise nord" "345" 0. 2 0. 005 1 Type. Urgence. ordinaire Lettre. tarif. Base. Le 0. 5 origine "nîmes" "igloo 17, banquise sud" "7775" 0. 4 0. 007 1 Type. Urgence. urgente Colis. tarif. Base. Co 2 Plusieurs attributs origine (1 par objet), un seul attribut Lettre. tarif. Base. Le un seul attribut Colis. tarif. Base. Co origine

Variable de classe • Peut-on faire ? public class Objet. Postal { private static

Variable de classe • Peut-on faire ? public class Objet. Postal { private static double tarif. Base; public double tarif. Base(){return Objet. Postal. tarif. Base; } } • Puis donner la valeur dans Lettre et Colis ? • Non: car c’est une unique variable (unique emplacement mémoire) donc elle ne peut pas avoir en même temps 2 valeurs différentes

Constante de classe • attribut de classe qui ne sera initialisé qu’une fois •

Constante de classe • attribut de classe qui ne sera initialisé qu’une fois • Final static public class Carre { private double largeur, hauteur; private static final int nombre. Cotes = 4; …. }

Méthode de classe • méthode se rapprochant d’une fonction (paradigme impératif) • pour gérer

Méthode de classe • méthode se rapprochant d’une fonction (paradigme impératif) • pour gérer des attributs static ou pour effectuer des calculs ne se rapportant pas à une instance • définie dans une classe • non héritée • appelée avec le nom de la classe comme receveur (pas this, ni super) • pas de liaison dynamique

Méthode de classe public class Personne { …. private static int nb. Personnes; public

Méthode de classe public class Personne { …. private static int nb. Personnes; public static int get. Nb. Personnes() { return nb. Personnes; } public static void set. Nb. Personnes(int nb. Personnes) { Personne. nb. Personnes = nb. Personnes; } public static void main(String[] s) { System. out. println(Personne. get. Nb. Personnes()); }

Méthode de classe (dans l’API) class Collections{ static (…) void sort(List<T> list) …. }

Méthode de classe (dans l’API) class Collections{ static (…) void sort(List<T> list) …. } … main … Array. List ma. Liste = …; Collections. sort(ma. Liste); class Math{ static double PI; static int max(int a, int b) …. } … main … double pi = Math. PI; …. System. out. println(Math. max(4, 8));