使用 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 3super 包装器
创建一个 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 反馈 — 无需本地设置。
此课程中的所有课时
- 使用 __index 链实现单继承
- 使用 super 调用父类方法
- 混入模式
- 方法重写和多态