Menu
CoddyTech

调用父类方法

本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 38 课,共 70 课。

有时,你并不想完全重写一个方法。你可能想要扩展父类的行为:先执行父类的操作,再添加一些额外内容。为此,你可以在子类的重写方法中直接调用父类的方法。

这种写法使用父类名称加点号的语法,并将 self 显式作为第一个参数传入:

local Animal = {}
Animal.__index = Animal

function Animal:new(name)
    local obj = {name = name}
    setmetatable(obj, Animal)
    return obj
end

function Animal:speak()
    print(self.name .. " makes a sound")
end

-- Dog 扩展了父类的行为
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})

function Dog:new(name)
    local obj = Animal.new(Animal, name)
    setmetatable(obj, Dog)
    return obj
end

function Dog:speak()
    Animal.speak(self)  -- 先调用父类方法
    print(self.name .. " wags tail happily")
end

关键代码行是 Animal.speak(self)。通过对父类使用点号语法并手动传入 self,我们会先调用原始方法,再添加自己的行为:

local buddy = Dog:new("Buddy")
buddy:speak()
-- 输出:
-- Buddy 发出叫声
-- Buddy 开心地摇尾巴

当父类方法执行了你不想重复的重要工作时,这种模式就很有用。子类可以先执行父类的逻辑,最后执行,甚至在自己的代码中途执行,让你完全掌控不同操作的组合方式。

challenge icon

挑战

简单

让我们构建一个问候系统,让 FormalPerson 扩展普通 Person 的行为!正式版本不会完全替换一个人自我介绍的方式,而是先进行标准的自我介绍,然后再加上一句礼貌的话。

你要将代码分成三个文件:

  • Person.lua:这是基类,包含一个用于保存姓名的 :new(name) 构造函数。添加一个 :introduce() 方法,打印这个人的姓名,后面接上 says hi。这是将被扩展的基础行为。
  • FormalPerson.lua:这是继承自 Person 的子类。它的构造函数应调用父类构造函数来处理姓名。重写 :introduce() 方法,但不要完全替换父类的行为,先调用父类的方法,使用 Person.introduce(self),然后再打印一行,内容是这个人的姓名,后面接上 bows politely。
  • main.lua:引入你的 FormalPerson 模块,从输入中读取姓名,创建一个 FormalPerson 实例,然后调用 :introduce(),查看两种行为组合后的效果。

你会收到一个输入:这个人的姓名。

对 FormalPerson 调用 :introduce() 时,应输出两行:第一行来自父类的方法,第二行来自子类添加的内容:

{name} says hi
{name} bows politely

例如,如果输入是 Eleanor,输出应为:

Eleanor says hi
Eleanor bows politely

关键在于,FormalPerson 不会重复实现“says hi”的逻辑。它会把这部分工作交给父类的方法,然后在此基础上添加自己的独特行为。这样可以让代码更简洁,也展示了子类如何扩展父类的功能,而不是替换它!

亲手试试

-- main.lua:问候系统的入口

local FormalPerson = require('FormalPerson')

-- 从输入中读取此人的姓名
local name = io.read()

-- TODO:用给定的姓名创建一个 FormalPerson 实例

-- TODO:调用 FormalPerson 实例的 introduce 方法
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Lua 在线编译器