类与继承

发布: (2026年2月4日 GMT+8 19:02)
7 min read
原文: Dev.to

Source: Dev.to

我们现在通过添加对 的支持来完成解释器。
类可以继承自其他类(重新使用它们的属性和方法),同时通过修改和添加实现自定义。

Classes diagram

我构建的

Commits bac3f3c, bfa82b9, 03b3af3, 6c70087

我理解的内容

  1. 类声明

    • 解析器为每个类声明创建一个 Stmt.Class 节点,节点中包含类名以及它的方法列表。
    • 解析器在当前作用域中声明类名,以便类能够在内部引用自身(例如用于递归)。
    • 当解释器访问 Stmt.Class 时,它会把 AST 节点转换为一个 LoxClass 对象,该对象保存一个方法映射(每个方法都被转换为 LoxFunction)。
  2. 类实例 / 对象

    • Lox 不使用 new 关键字。
    • LoxClass 实现了 LoxCallable 接口,所以类在被 调用 时会实例化。
    • 调用类会创建一个新的 LoxInstance 并返回它。
    • 实例保存对其所属类(LoxClass)的引用,以便以后能够查找方法。
  3. 属性

    • 字段不在类中声明,而是通过实例的赋值操作创建。
    • 当解释器求值得到一个 LoxInstance 对象时,它首先调用 get(),检查实例的字段映射。
    • 如果字段存在,则返回其值;否则解释器会查找方法。如果两者都未找到,则抛出运行时错误。
    • 解析器在看到 = 符号之前无法区分 setget 表达式。因此它先把左侧解析为普通表达式(get),在遇到 = 时再把该节点转换为 set 节点。
    • 当调用 set() 时,fields 映射会被更新(覆盖已有的键)。
  4. 方法

    • Lox 方法没有 fun 关键字,并且通过实例访问。
    • 如果方法被提取(例如赋值给变量)后再调用,this 必须仍然指向提取该方法的实例。
    • 为实现这一点,当访问方法时,LoxClass 会在底层的 LoxFunction 上调用 bind(instance)
    • bind() 会在方法原始闭包内部创建一个新的环境,在该环境中定义 this,并返回一个新的 LoxFunction
    • 如果在类外部使用 this,解析器(它会跟踪我们是否在类内部)会报告错误;否则它会把 this 当作普通局部变量来解析。
    • 解释器像查找其他变量一样在环境中查找 this,得益于绑定步骤,能够找到正确的实例。

Method binding illustration

  1. 构造函数

    • 用户自定义的构造函数叫做 init
    • LoxClass.call 发现 init 方法时,会立即调用它来初始化新实例。
    • 调用的参数个数(arity)会与初始化方法的参数数量进行检查。
    • init 总是返回 this(即使用户尝试返回其他值)。解析器通过在 LoxFunction 中跟踪 isInitialiser 标志来强制这一行为。
  2. 继承

    • 子类可以通过存储在子类的 LoxClass 中的引用继承超类的方法,方法查找会沿着继承链向上搜索。
    • 解析器现在会创建一个包含 superclass 字段(Expr.Variable)的 Stmt.Class 节点。
    • 解析器检查自继承的情况,如果检测到则报告错误。
    • 如果存在超类,解析器会创建一个新作用域并在其中定义 super,使得 super 对子类的所有方法可用。
    • 在创建子类时,解释器会创建一个新环境,将 super 指向超类,然后再创建方法。
    • 方法查找的顺序是:先检查实例的字段,其次是类的方法,最后是超类的方法(如果有)。如果链的末端为 null,则查找失败。

Inheritance diagram

auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fucafxqk8gaix3iou4h7h.png)

  1. super
    • super 关键字允许子类覆盖方法,同时仍然能够调用父类的原始实现。
    • 即使一系列子类都覆盖了同一个方法,super 也始终解析为最近的直接父类实现。

以上全部反映了在添加完整的类支持(包括继承、构造函数和 super 关键字)后,Jlox 解释器的当前状态。

类定义

  • 它后面跟着一个点和标识符(super.method),并被解析为 Expr.Super 节点。
  • 它在超类上查找方法,但将其绑定到当前实例(this),两者属于不同的环境。
  • 解析器将 super 视为局部变量,并计算它相对于定义 super 的环境的距离(跳数)。

Super lookup illustration

接下来:
对 Lox 的一些扩展——对列表和 for‑in 循环的支持。

随想

博客这个项目对我非常有帮助。虽然我目前正在做其他项目,但尝试解释我几个月前所做的事情(!)迫使我回忆细节和背后的理由。某种程度上,我之前获得的洞见仍在影响我当前的工作方式。

我非常喜欢记录我的所作所为,即使是最平凡的活动。在印度哲学中,工作是最高的奉献行为之一。无论我们做什么,只要带着意图去做,就会引导我们走向能力和精通,因为学习和实践的过程帮助我们成为最好的自己。泰戈尔说得好:

“Where tireless striving stretches its arms towards perfection…”

还有哪里能让上帝存在,除了那完美之中?因此,我提醒自己所有学到的东西。

Back to Blog

相关文章

阅读更多 »

面向对象编程 (OOPs)

类示例:Car 是一个类。不同的汽车可能有不同的名称和品牌,但它们都共享共同的属性,如四个轮子、速度限制和里程……

Java 特性::

Java 编程语言最初是为嵌入式系统、机顶盒和电视而开发的。根据需求,它被设计为能够在各种 p...

Java的特性

Java 特性封面图片 https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.am...