0Pricing
Lua Academy · 课时

使用 super 调用父类方法

显式调用基类方法,以实现协作式初始化。

使用 super 调用父类方法 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。

为什么要调用父类方法?

当子类覆盖父类方法但仍需要父类逻辑时,应显式调用父类方法。Lua 没有 super 关键字——您需要直接引用父类表:Parent.method(self, args)。

local Base={}
Base.__index=Base
function Base:describe()
  return "Base[name="..self.name.."]"
end

local Child={}
Child.__index=Child
setmetatable(Child,{__index=Base})

function Child:describe()
  local parentDesc = Base.describe(self)  -- super call
  return parentDesc .. " + Child[extra=true]"
end

local c=setmetatable({name="X"},Child)
print(c:describe())

构造函数中的父类调用

子类构造函数通过 Parent.new() 或 Parent._init(self,...) 调用父类构造函数。这样可以确保每个子类实例都执行父类初始化。

local Animal={}
Animal.__index=Animal
function Animal._init(self,name,sound)
  self.name=name; self.sound=sound; self.alive=true
end

local Dog={}
Dog.__index=Dog; setmetatable(Dog,{__index=Animal})
function Dog.new(name,breed)
  local self=setmetatable({},Dog)
  Animal._init(self,name,"Woof")  -- super init
  self.breed=breed
  return self
end

local d=Dog.new("Rex","Lab")
print(d.name,d.sound,d.breed)

协作式初始化

在深层继承层次中,每一层只初始化自己的字段,并调用父类的 init。这样可以让每个类只负责自己的字段。

local A={}
function A._init(self,x) self.x=x end

local B={}
function B._init(self,x,y) A._init(self,x); self.y=y end

local C={}
C.__index=C
function C.new(x,y,z)
  local self=setmetatable({},C)
  B._init(self,x,y)
  self.z=z
  return self
end

local c=C.new(1,2,3)
print(c.x,c.y,c.z)  -- 1 2 3

super 包装器

创建一个 super(self) 辅助函数,返回一个使用 self 调用父类方法的代理对象。与 Parent.method(self, ...) 相比,这能提供更简洁的语法。

local function super(self)
  local mt = getmetatable(getmetatable(self).__index)
  if mt then
    return setmetatable({}, {__index=function(_,k)
      local parentCls = mt.__index or mt
      return function(...) return parentCls[k](self,...) end
    end})
  end
end
print("super() helper pattern shown")

多个继承层级

存在多个继承层级时,每一层都会调用自己的直接父类。调用链会自动向上传递。

local L1={} L1.__index=L1
function L1:intro() return "L1" end

local L2={} L2.__index=L2
setmetatable(L2,{__index=L1})
function L2:intro() return L1.intro(self).."+L2" end

local L3={} L3.__index=L3
setmetatable(L3,{__index=L2})
function L3:intro() return L2.intro(self).."+L3" end

local obj=setmetatable({},L3)
print(obj:intro())  -- L1+L2+L3

扩展方法行为

扩展父类方法:先调用父类版本,然后添加额外行为。这相当于没有正式 AOP 框架时的“前置”或“后置”通知。

local Logger={} Logger.__index=Logger
function Logger:log(msg) print("[LOG] "..msg) end

local TimedLogger={} TimedLogger.__index=TimedLogger
setmetatable(TimedLogger,{__index=Logger})

function TimedLogger:log(msg)
  local ts=os.date("%H:%M:%S")
  Logger.log(self,"["..ts.."] "..msg)  -- augment
end

local tl=setmetatable({},TimedLogger)
tl:log("Server started")

避免父类调用陷阱

进行父类调用时,始终使用确切的父类名称,而不要使用 getmetatable(self).__index——元表查找可能返回中间类,而不是真正的父类。请明确指定,以确保清晰性和正确性。

-- Correct: explicit parent reference
function Child:init(x)
  Base.init(self, x)  -- explicit, clear
end

-- Fragile: uses metatable lookup
function Child:initBad(x)
  local parent = getmetatable(getmetatable(self).__index)
  -- This may not be Base if hierarchy changes!
end
print("Be explicit with parent class references")

为 __tostring 调用父类方法

在子类中覆盖 __tostring,同时包含父类的表示形式,从而生成完整的描述。

local Base={}
Base.__index=Base
Base.__tostring=function(self) return "Base("..self.name..")" end

local Child={}
Child.__index=Child
setmetatable(Child,{__index=Base})
Child.__tostring=function(self)
  local baseStr = Base.__tostring(self)
  return baseStr.." + Child(level="..self.level..")"
end

local c=setmetatable({name="X",level=5},Child)
print(tostring(c))  -- Base(X) + Child(level=5)

受保护的模板方法

模板方法模式:基类定义一个算法框架,并调用抽象步骤;子类实现这些步骤。

local Processor={} Processor.__index=Processor
function Processor:run(data)
  data = self:preProcess(data)
  data = self:process(data)
  return self:postProcess(data)
end
function Processor:preProcess(d) return d end
function Processor:postProcess(d) return d end
function Processor:process(d) error("implement process()") end

local Upper={} Upper.__index=Upper
setmetatable(Upper,{__index=Processor})
function Upper:process(d) return d:upper() end

local p=setmetatable({},Upper)
print(p:run("hello"))  -- HELLO

父类调用约定

约定:在仅供内部使用的 init 函数前加上 _(例如 _init),并且只在顶层构造函数中公开 new()。这样可以使继承链保持清晰。

-- Convention summary:
-- Animal._init(self, ...)  -- used for super calls
-- Dog.new(...)             -- only public constructor
-- Never call Dog._init from outside

local Foo={} Foo.__index=Foo
function Foo._init(self,x) self.x=x end
function Foo.new(x)
  return setmetatable({},Foo):_init(x) or
    (function() local s=setmetatable({},Foo); Foo._init(s,x); return s end)()
end
print("Convention: _init for super, new() for public")

父类调用问题

如何在 Lua 中调用父类方法?

回顾:父类调用

始终通过显式的类引用调用父类方法:Parent.method(self, ...)。在多个继承层级的协作式构造函数中,使用 _init 约定。

常见问题解答

「使用 super 调用父类方法」课时是免费的吗?

是的 — 「使用 super 调用父类方法」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。

「使用 super 调用父类方法」这节课中我会学到什么?

显式调用基类方法,以实现协作式初始化。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Lua Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「使用 super 调用父类方法」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Lua Academy 课中编写并运行代码吗?

能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 __index 链实现单继承
  2. 使用 super 调用父类方法
  3. 混入模式
  4. 方法重写和多态
← 返回 Lua Academy