如何覆盖类中字段的默认值?

时间:2018-10-07 07:55:23

标签: class inheritance field racket default-value

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

(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的班级系统旨在支持的预期方式是什么?

1 个答案:

答案 0 :(得分:2)

首先,您的示例做了您可能不想要的事情。使用init-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。如果您愿意,您当然可以支持,但是,有了宏,您和库编写者都不必预见您可能想要的所有可能的便利。编写一个简单的宏以使您的特定代码更具可读性是完全合法的。