如何在smalltalk中为实例变量指定class

How to specify class for instance variable in smalltalk

可能是我对smalltalk的概念了解不够,但是我的问题是:

我正在创建一个 class,我们称它为 Main-class,它有一个实例变量 bar,我想成为某个 class - Other-class,并使用这个 Other-class ' Main-class 方法中的方法有点像这样:

    Object subclass: #Other-class
        instanceVariableNames: 'foo'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Custom-class'

setFoo: newFoo

    foo := newFoo.

Main-class :

  Object subclass: #Main-class
        instanceVariableNames: 'bar'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Custom-class'

newBar: val

    bar := Bar new. 
    bar setFoo: val.

显然,我得到一个错误,提示没有 setFoo 方法。 但是我如何指定我希望 bar 成为特定 class 的变量,然后在其上使用 class' 方法?

关于问题的标题:你不知道。 Smalltalk 是一种动态类型语言,这意味着一个变量的值在不同的时间点可以有不同的类型——没有为变量声明类型。但对于人类,您可以在 class 注释中注明变量的实际类型(请参阅系统浏览器中 instance-side 和 class-side 按钮旁边的 ? 按钮)。

关于您的问题:您必须确保分配给实例变量 bar 的 object 是合适的类型。在这种情况下,它必须理解消息 setFoo:。在您的第二个代码片段中,这意味着:

  1. 你应该使用另一个可以响应 setFoo: 的 class 而不是 Bar,例如 Other-class(顺便说一句,这是一个奇怪的名字,因为连字符,您不能在您的方法中轻松引用它,请改用 CamelCase) 来填充变量,或
  2. setFoo: 必须为 class Bar 实施。

respondsTo: 方法可用于确定对象是否响应消息:

bar respondsTo: #setFoo
  ifTrue: [bar setFoo: val]
  ifFalse: [missiles launch]

如果您不想在 bar 不理解 setFoo 时发射导弹,那么请决定您想要发生什么。也许您的程序应该因错误而崩溃。这不像发射导弹那么极端,但仍然有点极端,而且很少是你真正想要的,特别是如果 其他人正在使用你的软件。

选项:只需使用正确的对象类型

ifFalse: [bar := ClassWithSetFoo new; setFoo: val]

选项:通过修改其 Class

来扩展对象以执行您需要的操作
ifFalse: [ (bar class) addInstVarName: 'foo'.
           (bar class) compile: 'setFoo: newFoo [foo := newFoo]'.
           bar setFoo: val. ]

第二个选项可能是也可能不是您想要的,具体取决于上下文。即使不是,它也说明了 Smalltalk 动态类型的强大功能。 Rails 上 Ruby 的大量 "magic" 是使用类似的策略构建的。这是可能的,因为 Ruby 对象与 Smalltalk 的对象在哲学上有很多相似之处。