|
| Postscript, PDF | Didier Rémy | Polytechnique, INRIA |
|
|
||||
|
|
|
Attention! Une liaison peut en cacher une autre! mais ne peut pas la
changer. |
|
|
|
|
|
|
|
A, une méthode m1 peut appeler une méthode
m2 de la même classe en envoyant un message m2 à
self. Cela simule un appel récursif, mais le câblage de cet appel
ne sera réalisé qu'au moment de la fabrication de l'objet.B de A peut redéfinir la méthode
m2. Dans un objet de B, c'est la nouvelle définition de
m2 qui sera appelé par la méthode m1.| La liaison tardive est au coeur de la programmation avec objets, de son expressivité , mais aussi de ses difficultés. |
|

![]() |
![]() b est privée et cachée ou bien
la classe B contient un object instance de A. |
|
| · | Lorsqu'on ignore l'implémentation d'une classe parente, il est impossible de connaître l'effet de la redéfinition d'une méthode sur les autres méthodes, ce qui peut avoir des conséquences très inattendues. |
| · | La liaison tardive est délicate, souvent difficile à contrôler, mais c'est sans doute le prix à payer pour l'expressivité. L'héritage repose sur l'optimisme (que la classe parente fera bien ce que l'on imagine à la seule vue de son interface). |
|
| En Ocaml, les variables d'instances sont héritées mais ne sont jamais en liaison tardive. |
|
|
| En Ocaml, les méthodes visibles sont toujours en liaison tardive |
| Méthode privée º pas encore utilisée ``en public'' |
| (eg. pas encore d'appel externe) |
|
| · | Une méthode finale ne peut plus être redéfinie (cela produira une Erreur de type). |
| · | On peut appeler la méthode d'une classe particulière. |
| · | Utiliser une fonction plutôt qu'une méthode (on utiliserait une méthode statique en Java). |
| · | Utiliser une méthode privée cachée auxiliaire. |
| · | Remplacer l'héritage par la délégation: créer un objet de la classe parente A dans la sous-classe B. |
|
a
en redéfinition une méthode b dans une sous-classe, on peut utiliser:
| · | une méthode privée (figure de gauche), ou |
| · | une fonction auxiliaire (figure de droite) » méthode statique: |
|
|
|
a
par inadvertance, ie. en redéfinissant celui de b.
|
|
b de briser les invariants de la
classe d'origine en fabriquant une classe relais où les méthodes
délèguent leur exécution aux méthodes d'une instance de la
classe d'origine.
|
|
|
|
|
clé dans une sous classe n'affectera
pas la version privée clé_cachée.
|
|
|
|
|
|
|
0.
|
|
|
|
hello est passé à la méthode eval, donc coercé en un
Object et passé à surcharge avec le type statique
Objet. Le compilateur choisi donc la première définition et
Programme.call retourne
true
|
|
hello est directement passé à surcharge avec le type
String. Le compilateur choisit donc la deuxième définition.eval est (à tord) de
propager mentallement la surcharge conduisant au programme ci-dessus.
|
|
hello est directement passé à surcharge avec le type
Object, comme dans la version de référence. Le compilateur choisit
donc la première définition.
|
|
x.bin(y) pour chacune des 9
combinaisons possibles de "(x,y)"?
Écrire un programme Java qui permet de vérifier les cas ci-dessus
(en calculant les 9 combinaisons).
On considére maintenant la définition suivante:
|
B.
Répondre à la première question mais avec la définition ci-dessus.
Écrire une variante qui retourne toujours (et seulement) 2 lorsque les
deux objets sont de la classe B (et seulement 1 autrement)
La surcharge peut être éliminée statiquement en choisissant des nom
non-ambigü (par exemple en suffixant les nom par le type des arguments):
écrire une version du programme de la question 2 qui n'utile pas la
surcharge.
Quelle version de bin_A ou bin_B faut-il utiliser pour les
différents appels (9 combinaisons possibles) pour être le plus précis
possible? (On indiquera par une prime si la version exécutée est celle
définie dans la class A ou dans la class B).
|
|
|
Héritage multiple
![]() |
Héritage simple
![]() |
|
|
|
|
|
|
|
|
|
class w (arg : arg_type) = body end class b = w c |
|
b ne voit que les méthodes de arg_type et de body
(pas de arg).
|
|
|
|
|
new c1 par new c1(object end). Si le type de s doit être fixé, ce qui est en général le
cas, cette solution est restreinte et ne remplace pas l'héritage multiple. |
|
| -- | Il faut prévoir le besoin d'abstraction. |
| -- | Il faut connaître l'interface de l'argument (les composantes non spécifiées qui peuvent être oubliées seront cachées). |
| + | Avantage: le câblage (liaison de super, overriding) peut-être fait dans le wrapper et être partagé. |
| + | Les variables de la classe parente sont visibles. |
| + | Rien à prévoir |
| + | Il y a toujours une classe parente hypothétique (héritage) |
| -- | Le câblage doit être réalisé après coup à chaque utilisation |
| -- | Les variables ne peuvent pas être virtuelles |
|
|
this.
En Ocaml, on doit déclarer en tête de la classe une variable pour désigner
cet objet. Nous parlerons de self ici quelque soit le mécanisme de
liaison.self c'est envoyer un message à l'objet en train
d'exécuter une méthode, ce qui réalise la récursion. Parce que cette
méthode est prise dans l'objet à l'exécution (du moins en théorie, le
compilateur étant libre de compiler l'appel autrement pourvu que son effet
soit indiscernable) et non dans la classe, un message à self
effectue une liaison tardive.
|
|
c en faisant:
|
'a d'un objet qui a une méthode m
qui retourne un objet de type 'a.
|
|
|
this garde
dans une classe héritée le type de la classe parente.
|
|
|
Error serait correctement typée.
(on écrira le code Ocaml correspondant)
|
(< m1 : int; m2 : 'a > as 'a) avec le mot clé as.
Ici, la variable 'a sert uniquement à décrire la
récursion: elle n'est pas polymorphe car il n'y a pas d'autre
variable dans le membre gauche.(< m1 : int; m2 ; 'a; ..> as 'a) est
polymorphe, car .. représente une variable de type anonyme.
|
|
|
Point ayant
un champ x de type int et une méthode self de type
Point.
|
| · | les méthodes qui retournent self. |
| · | les méthodes binaires. |
< l_1 = e_1; ... l_p = e_p > permet de retourner une
copie de self dans lesquels les variables l_i sont liée aux valeurs
résultant de l'évaluation de e_i. Oo.copy ne permet pas de changer les variables d'instances.)
|
|
démon ci-dessus. gènes et
la variable d'intance non mutable au_delà dont le contenu est une
référence?
Mettre en évidence le comportement de au_delà sur une exemple
permettant de communiquer entre un certains groupes d'objets que l'on
précisera.
Quel est le rôle population?
Quel est la différence entre les méthodes même et clone?
Pourquoi mutation n'appelle pas contrôle?
Les démons peuvent-ils être en surpopulation?
Qui a-t-il de remarquable dans ce cas?
Quel est la différence entre la méthode copie et même
À quoi sert la méthode même?
Quel est la différence entre combine et mute?
Est-ce que mute pourrait être une fonction auxiliaire comme
combine? Quel est le point commun.
|
|
| On peut combiner sous-typage structurel et déclaré. |
|
|
A peut être utilisé comme un élément de type B, alors
une fonctions dont l'argument est de type B peut être coercée en une
fonction dont l'argument est de type A.A peut être vu par
sous-typage comme un argument de type B, donc passé à la fonction.
|
|
transformation est non-variant, car pour
qu'une A transformation soit plus petit qu'une B transformation,
il faudrait que A -> A soit plus petit que B -> B, ie. que
A soit à la fois plus grand et plus petit que B. Ce qui n'est
vrai que pour A égal à B (ici, on suppose que la relation de
sous-typage est anti-symmétrique).
|
'a buffer défini par
|
'a -> unit et unit -> 'a.
Ainsi le constructeur de référence 'a ref est non-variant.
|
| · | Si A' < A et B < B' alors A ® B < A' ® B', |
| · | Si Ai < Bi alors á l1: A1; ...lk:Ak; ... ln : Anñ < á l1: B1; ...lk:Bkñ |
['a] cell des cellules (version objet
des références) de type 'a'.
Expliquer pourquoi le type 'a cell est non-variant par rapport à
'a.
Vérifier expérimentalement (en essyant différentes coercions)
que c'est bien le cas.
Vérifier qu'en cachant certaines méthodes (donc en perdant certaines
fonctionnalités), la classe redevient co-variante ou contra-variante.
Le type des objets d'une classe héritée de ['a] cell peut-il être
sous-type de [a'] cell (si oui, donner un exemple)?
|

|
|
|
|
a et b dont ont veux mettre les objets dans
une liste avec l'interface c.
|
|
|
|
|
m
appelle cette méthode et retourne son argument:
|
m, elle retourne un objet du
même type de son argument< m : 'b > pour le type de l'argument, mais dans ce cas, on ne peut
retourner que le type < m : 'b>.
|
(f : All ('a <: <m : 'b >) 'a -> 'a)
|
instanceof combinée avec cast.
|
|
implement crée une relation de sous-typage sans
relation d'héritage.
|
|
|
|
self.
Par extension, une méthode est binaire si son type
contient une occurrence contra-variante du type de self.
|
|
| · | Il est difficile de leur préserver un type correct dans une sous-classe. |
| · | Le type des objets avec une méthode binaire n'admet pas de sous-type tant qu'un méthode binaire reste visible. |
|
|
|
|
|
|
p = new point 0 et q = new point_coloré 0 0
(pour les classes définies ci-dessus)et
en oubliant temporairement le typage (on ne pourra donc pas les tester en
Ocaml) quelles sont les combinaisons de x.max y qui s'évaluerait quand
même correctement? La méthode max de la classe point_coloré lit indirectement, par un
appel à la méthode inf variable y de son argument donc son argument
doit être de la classe point_coloré. |
|
q peut être vu avec l'interface d'un
point coloré, sinon, on pourrait appeler sa méthode max avec p.
|
gety est indirect.| (1) | les méthodes implémentées, |
| (2) | les méthodes utilisées récursivement. |
| (3) | les méthodes utilisées dans un objet du même type que self. |
getx ou gety.| (1) | peuvent être oubliées: elles ne seront simplement pas
disponibles. (par exemple max)
|
| (2) | sont prises dans l'objet et non dans l'argument, et donc
toujours présentes. (par exemple inf)
|
|
|
|
|
|
|
| · | Elle devient plus délicate et moins bien adaptée dès qu'il y a besoin
d'utiliser des méthodes binaires. |
| · | En générale, celles-ci détruisent la symétrie, par exemple plus x y
devient x#plus y.
|
This document was translated from LATEX by HEVEA and HACHA.