返回介绍

6.5 发消息给超类

发布于 2025-02-20 00:17:08 字数 3516 浏览 0 评论 0 收藏 0

当某个方法覆盖(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 的定义中重用和扩展 Pointas-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)

这个程序返回什么?我们来研究一下。 -> 展开为发送 lookupc 的类,也就是 C 。在 C 中没有 m 方法,所以转而发送 lookup 给其超类, BB 找到 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文