|
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.