首页 > 解决方案 > 如何覆盖类中字段的默认值?

问题描述

这是迄今为止我发现的覆盖类中默认字段值的最佳方法:

(define sa-canvas%
  (class canvas%
    . . .
    (init-field [min-width 600]
                [min-height 300]
                [stretchable-width #f]
                [stretchable-height #f])
    (super-new [min-width min-width]
               [min-height min-height]
               [stretchable-width stretchable-width]
               [stretchable-height stretchable-height])
    . . .))

这对于 Racket 来说似乎太冗长了。Racket 的类系统旨在支持的预期方式是什么?

标签: classinheritancefieldracketdefault-value

解决方案


首先,您的示例做了您可能不想要的事情。Usinginit-field在类的实例上创建一个可变的公共字段,该字段已初始化为相应的初始化参数。这些并不经常使用:更常见的是使用私有字段(即define)和 getter 方法,如果您想支持突变,可能使用 setter。事实上,我们可以看出,但是canvas%使用这些初始化参数,它不会将它们存储为公共字段,因为(与方法一样)一个类不能创建一个与它的超类的字段具有相同外部名称的新字段. (字段可以用 . 继承inherit-field。)例如,评估这个表达式会引发一个异常:

(class (class object%
         (init-field [x 1])
         (super-new))
  (init-field [x 2])
  (super-new [x x]))

对于您不想作为字段保留的初始化参数,请使用init而不是init-field. 此表达式与您的示例相同,但不创建字段:

(class canvas%
  (init [min-width 600]
        [min-height 300]
        [stretchable-width #f]
        [stretchable-height #f])
  (super-new [min-width min-width]
             [min-height min-height]
             [stretchable-width stretchable-width]
             [stretchable-height stretchable-height]))

不过,您是对的,这是要保持同步的大量输入和繁琐的代码。类库没有内置不同的方法,但是您可以使用一个小宏轻松地自己添加一个:

(class canvas%
  (define-syntax-rule (init/super-new [name default] ...)
    (begin (init [name default] ...)
           (super-new [name name] ...)))
  (init/super-new
   [min-width 600]
   [min-height 300]
   [stretchable-width #f]
   [stretchable-height #f]))

可以很容易地进入define-syntax-rule模块级别,但是:

  1. 本地宏很酷;和

  2. 宏的一大优点是您不必处理一般情况:比如说,有时您可能还想将其他参数传递给super-new. 如果您愿意,您当然可以支持这一点,但是,对于宏,您和库编写者都不必提前预见您可能想要的所有可能的便利。编写一个简单的宏来使您的特定代码更具可读性是完全合法的。


推荐阅读