Files
endorphin-tabularium-docs/content/docs/smalltalk/03-classes-and-methods.md
T

2.9 KiB
Raw Blame History

类、方法与元类:行为放在哪里

原书依据:第 3 章和第 5 章,印刷页 9–12、21–25,PDF 页 20–23、32–36。

状态的可见范围

名称 用途 阅读时应问
实例变量 每个实例自己的状态槽位 是否继承自父类?
类变量 由类及相关子类共享的绑定 是否存在跨实例影响?
全局变量 经系统字典访问的绑定 是否形成隐含依赖?
池变量 多个类共享的变量集合 类定义引用了哪个池?
参数、临时变量 一次方法或块计算的局部名称 生命周期与可赋值规则是什么?

原书第 5 章明确限制对方法参数再赋值。本教程沿用不修改参数的写法,不把其他实现的语法扩展套回原书。

用一个小类串起语法

以下为本文编写的历史风格定义。类定义在 Browser 或相应定义工具中提交;后续方法分别安装在实例侧,不是把整段当作 Workspace 表达式执行。

Object subclass: #StudyCounter
    instanceVariableNames: 'count'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'Study-Examples'

实例侧方法:

initialize
    count := 0
increment
    count := count + 1
value
    ^count

工作区中的使用示意:

| counter |
counter := StudyCounter new.
counter initialize.
counter increment.
counter value

这里显式初始化,不依赖 new 是否自动发送 initialize。这些安装步骤和类定义消息需要在目标镜像核对。increment 没有显式返回,所以返回 counter;value 返回槽位保存的数值。

方法查找

普通消息从接收者的类开始找选择子,找不到就沿父类向上查找。原书的根类为 Object。整个链中没有对应方法时,进入 doesNotUnderstand: 协议。

重写方法只改变行为的选择,不把接收者换成父类对象。self 始终指向当前接收者。

super 改变查找起点

假设 B 继承 A,C 继承 B;方法 describe 定义在 B 中。如果该方法发送 super name,查找从 A 开始,即便当前接收者是 C 的实例。若发送 self name,查找从接收者实际的类 C 开始。

因此 super 不是“父对象”。它保留同一接收者,按方法定义位置确定查找起点。

实例侧与类侧

StudyCounter new 的接收者是类对象;counter increment 的接收者是它的实例。类自身也是对象,类侧方法由元类组织。

读类侧构造方法时,要检查它究竟分配实例、初始化状态还是返回缓存对象。new 只是发给类对象的消息,不能把它视为所有类都必须遵循的固定语法。

原书的 primitive 方法把部分操作交给虚拟机。追踪执行路径时,需区分 Smalltalk 方法体与 VM 提供的原语;本教程没有验证任何 VM 的原语源码。