最简单的方法是直接将方法附加到它,而不是使用元表。
local function extend(super_instance)
super_instance.newMethod = newMethod
return super_instance
end
local function createExtended(...)
return extend(createSuper(...))
end
这将起作用,除非您的超类使用 __newindex(例如,阻止您写入未知属性/方法),或者使用 pairs 或 next 迭代键,因为它现在将有一个额外的键。
如果由于某种原因您无法修改对象,则必须将其“包装”起来。
您可以创建一个新实例,将其所有方法、属性和运算符“代理”到另一个实例,但它会添加额外的字段和方法。
local function extend(super_instance)
local extended_instance = {newMethod = newMethod}
-- and also `__add`, `__mul`, etc as needed
return setmetatable(extended_instance, {__index = super_instance, __newindex = super_instance})
end
local function createExtended(...)
return extend(createSuper(...))
end
这适用于简单的类,但不适用于所有用途:
像pairs 和next 这样的表迭代不会从原始表中找到键,因为它们实际上并不存在。如果超类检查给定对象的元表(或者如果超类实际上是用户数据),它也将不起作用,因为您会找到扩展元表。
但是,许多纯 Lua 类不会做这些事情,所以这仍然是一个相当简单的方法,可能对你有用。
你也可以做一些类似于 Go 的事情;无需“扩展”类,您只需将该类作为字段嵌入,并为直接调用包装类上的方法提供便利,这些方法只调用“扩展”类上的方法。
由于“方法”在 Lua 中的工作方式,这有点复杂。你无法判断一个属性是一个属性的函数还是它实际上是一个方法。下面的代码假定所有带有type(v) == "function" 的属性实际上都是方法,这通常是正确的,但实际上可能并不适合您的具体情况。
在最坏的情况下,您可以手动维护要“代理”的方法/属性列表,但取决于您需要代理多少类以及它们拥有多少属性,这可能会变得笨拙。
local function extend(super_instance)
return setmetatable({
newMethod = newMethod, -- also could be provided via a more complicated __index
}, {
__index = function(self, k)
-- Proxy everything but `newMethod` to `super_instance`.
local super_field = super_instance[k]
if type(super_field) == "function" then
-- Assume the access is for getting a method, since it's a function.
return function(self2, ...)
assert(self == self2) -- assume it's being called like a method
return super_field(super_instance, ...)
end
end
return super_field
end,
-- similar __newindex and __add, etc. if necessary
})
end
local function createExtended(...)
return extend(createSuper(...))
end