6.5 发消息给超类
当某个方法覆盖(override)超类中的方法时,有时候需要能调用超类中的定义。允许这么做就可以支持许多典型的改进模式,例如在执行方法之前或之后添加要做的事情,比如对其参数和返回值的进一步处理等等。这被称作 给超类发送 (super send)。我们选择 -->
作为给超类发送的语法。
先来看一个例子:
(define Point
(CLASS extends Root
([field x 0])
([method x? () (? x)]
[method x! (new-x) (! x new-x)]
[method as-string ()
(string-append "Point("
(number->string (? x)) ")")])))
(define ColorPoint
(CLASS extends Point
([field color 'black])
([method color? () (? color)]
[method color! (clr) (! color clr)]
[method as-string ()
(string-append (--> as-string) "-"
(symbol->string (? color)))])))
> (define cp (new ColorPoint))
> (-> cp as-string)
"Point(0)-black"
请注意,给超类发送使我们能够在 ColorPoint
的定义中重用和扩展 Point
中 as-string
的定义。在 Java
中,这是通过对 super
调用方法来完成的,但究竟 super
是什么?给超类发送的语义是什么?
首先要澄清的是:给超类发送的接收者是啥?在上面的例子中,当使用 -->
时, as-string
发送给了哪个对象? self
!事实上, super
只影响了方法查找。一个常见的误解是,在执行给超类发送时,方法查找从接收方的 超类 开始,而不是从它的类开始。我们来构造一个小例子,看看为什么这是不正确的:
(define A
(CLASS extends Root ()
([method m () "A"])))
(define B
(CLASS extends A ()
([method m () (string-append "B" (--> m) "B")])))
(define C
(CLASS extends B () ()))
(define c (new C))
(-> c m)
这个程序返回什么?我们来研究一下。 ->
展开为发送 lookup
给 c
的类,也就是 C
。在 C
中没有 m
方法,所以转而发送 lookup
给其超类, B
。 B
找到 m
对应的方法,并返回之。下一步调用此方法,第一个参数是当前的 self
(也就是 c
),接下来是消息的参数,在这里为空。对这个方法求值就需要对 string-append
的三个参数求值,其中第二个参数是给超类发送。如果使用上述给超类发送的定义,那么 m
不是在 C
(接收方的实际类)中查找,而是在 B
(它的超类)中查找的。 B
中有 m
方法吗?是的,我们正在执行的就是它……换句话说,如果这么理解 super
,上述程序将 不会 终止。
一些动态语言,比如 Ruby,允许在运行时改变类的继承关系。这在基于原型的语言(如 Self 和 JavaScript)中很常见。
错在哪里?给 self
发送时,不应该在接收方的超类中查找方法。在这个例子中,我们应该在 A
而不是在 B
中查找 m
。为此,我们需要知道执行给超类发送的方法的 宿主类的超类 。这个值应该是在方法体中静态绑定还是动态绑定的?我们刚才已经说过了:它是方法的宿主类的超类,不可能动态改变(至少在我们的语言中如此)。好在在方法的词法环境中,已经有了指向超类的绑定, scls
。所以,我们只需要引入新的局部宏 -->
,其展开请求超类 scls
来查找消息。 -->
可以被用户代码使用,所以它要被添加到 #:captures
标识符列表中:
(defmac (CLASS extends superclass
([field f init] ...)
([method m params body] ...))
#:keywords field method extends
#:captures self ? ! -->
(let* ([scls superclass]
[fields (append (scls 'all-fields)
(list (cons 'f init) ...))]
[methods
(local [(defmac (? fd) ....)
(defmac (! fd v) ....)
(defmac (--> md . args) #:captures self
(((scls 'lookup 'md) self) . args))]
....)])))
请注意, lookup
现在被发送到当前正在执行的方法的宿主类的超类 scls
,而不是当前对象的实际类。
> (define c (new C))
> (-> c m)
"BAB"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论