【问题标题】:What's the equivalent of constructors in CLOS?CLOS 中构造函数的等价物是什么?
【发布时间】:2013-04-18 17:47:03
【问题描述】:

你会如何在 Lisp 中表达以下 Java 代码?

class Foo {
    private String s;

    public Foo(String s) {
        this.s = s;
    }
}

class Bar extends Foo {
    public Bar(int i) {
        super(Integer.toString(i));
    }
}

在 Lisp 中,make-instanceinitialize-instance 是否等同于构造函数?如果是,如何调用超类构造函数?

【问题讨论】:

    标签: lisp common-lisp clos


    【解决方案1】:

    在方法中使用CALL-NEXT-METHOD 来调用下一个方法。

    通常只使用标准方法组合工具并为INITIALIZE-INSTANCE 编写:BEFORE:AFTER:AROUND 方法。

    一种方式:

    (defclass foo ()
      ((s :type string :initarg :s)))
    
    (defclass bar (foo) ())
    
    (defmethod initialize-instance :around ((b bar) &key i)
      (call-next-method b :s (princ-to-string i)))
    

    例子:

    CL-USER 17 > (make-instance 'bar :i 10)
    #<BAR 402000B95B>
    
    CL-USER 18 > (describe *)
    
    #<BAR 4130439703> is a BAR
    S      "10"
    

    请注意,我已经调用了CALL-NEXT-METHOD,而没有更改它分派的参数。我只是更改了&amp;key initarg 参数。

    【讨论】:

    • Doesn't CALL-NEXT-METHOD 只是用相同的参数调用下一个方法。我将如何实现我的示例,即将整数参数转换为字符串并将其传递给超类方法?
    • @Tobias Brandt:看我的例子
    【解决方案2】:

    这里是 Rainier 示例的放大版,增加了一点小改动: 通过约束槽值进行专业化(子类化),至少在 施工时间。考虑一个用于二维矩形的类 m-box

    (defclass m-box ()
      ((left   :accessor m-box-left   :initform 0 :type integer :initarg :left  )
       (top    :accessor m-box-top    :initform 0 :type integer :initarg :top   )
       (width  :accessor m-box-width  :initform 0 :type integer :initarg :width )
       (height :accessor m-box-height :initform 0 :type integer :initarg :height)))
    

    我们可以这样尝试:

    (describe (make-instance 'm-box :left 42 :top -39 :width 5 :height  11))
    
    : #<M-BOX {10047A8F83}>
    :   [standard-object]
    : 
    : Slots with :INSTANCE allocation:
    :   LEFT    = 42
    :   TOP     = -39
    :   WIDTH   = 5
    :   HEIGHT  = 11
    

    现在考虑一个子类或特化:让一个 m-block 是一个 m-box 单位宽度和高度。我们设置initialize-instance方法通过 lefttop 的值,但不包括 widthheight

    (defclass m-block (m-box) ())
    (defmethod initialize-instance
        :around
        ((mb m-block)
         &key (left 0) (top 0))
      (call-next-method mb :left left :top top :width 1 :height 1))
    

    我们可以如下创建一个m-block的实例:

    (describe (make-instance 'm-block :left 17 :top -34 :width 5 :height  11))
    
    : #<M-BLOCK {10049C0CC3}>
    :   [standard-object]
    : 
    : Slots with :INSTANCE allocation:
    :   LEFT    = 17
    :   TOP     = -34
    :   WIDTH   = 1
    :   HEIGHT  = 1
    

    构造函数不会阻止用户尝试设置widthheight, 就像处理一些不存在的插槽一样:

    (describe (make-instance 'm-block :left 17 :top -34 :plugh 2345))
    
    Invalid initialization argument:
      :PLUGH
    in call for class #<STANDARD-CLASS COMMON-LISP-USER::M-BLOCK>.
       [Condition of type SB-PCL::INITARG-ERROR]
    

    但构造函数确实用 1 纠正了用户的无效输入。

    如果 用户尝试输入无效的 widthheight

    (defclass m-block (m-box) ())
    (defmethod initialize-instance
      :around
      ((mb m-block)
       &key (left 0) (top 0) (width 1) (height 1))
      (when (or (/= 1 width) (/= 1 height))
        (error "an m-block must have unit width and height"))
      (call-next-method mb :left left :top top :width 1 :height 1))
    

    用户的以下尝试被拒绝:

    (describe (make-instance 'm-block :left 17 :top -34 :width 5 :height  11))
    
    an m-block must have unit width and height
       [Condition of type SIMPLE-ERROR]
    

    但是这个也演示了 height 的默认设置,通过:

    (describe (make-instance 'm-block :left 17 :top -34 :width 1))
    
    : #<M-BLOCK {1005377983}>
    :   [standard-object]
    : 
    : Slots with :INSTANCE allocation:
    :   LEFT    = 17
    :   TOP     = -34
    :   WIDTH   = 1
    :   HEIGHT  = 1
    

    此示例允许用户在之后setf 宽度或高度。我不 知道如何在子类的实例中使宽度和高度只读 而不是在超类的实例中。

    【讨论】:

      猜你喜欢
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2020-02-13
      • 2019-12-07
      • 2023-01-17
      • 1970-01-01
      • 2011-12-11
      • 1970-01-01
      相关资源
      最近更新 更多