如何在Lua中创建类、子类和属性?
我很难理解Lua中的类。徒劳无功的谷歌搜索让我想到了关于元表的想法,并暗示需要第三方库来模拟/编写类。
这是一个示例(因为我注意到我提供示例代码时会得到更好的答案):
public class ElectronicDevice
{
protected bool _isOn;
public bool IsOn { get { return _isOn; } set { _isOn = value; } }
public void Reboot(){_isOn = false; ResetHardware();_isOn = true; }
}
public class Router : ElectronicDevice
{
}
public class Modem :ElectronicDevice
{
public void WarDialNeighborhood(string areaCode)
{
ElectronicDevice cisco = new Router();
cisco.Reboot();
Reboot();
if (_isOn)
StartDialing(areaCode);
}
}
这是我尝试使用Javier建议的技术翻译上面的内容的第一次尝试。
我采纳了RBerteig的建议。然而,派生类上的调用仍然产生:“尝试调用方法“methodName”(零值)”的错误消息。
- - 一切都是表
ElectronicDevice = {};
- - 奇迹发生了
mt = {__index=ElectronicDevice};
- - 这必须是构造函数
function ElectronicDeviceFactory()
- - 似乎元表保存了字段
return setmetatable({isOn=true},mt)
end
- - 使用get / set函数模拟属性
function ElectronicDevice:getIsOn()return self.isOn 结束
function ElectronicDevice:setIsOn(value)self.isOn = value end
function ElectronicDevice:Reboot()self.isOn = false;
self:ResetHardware(); self.isOn = true; end
function ElectronicDevice:ResetHardware()print( 'resetting hardware ...') 结束
Router = {};
mt_for_router = {__index=Router}
- - Router继承自ElectronicDevice
Router = setmetatable({},{__index=ElectronicDevice});
- - 子类的构造函数,不确定元表是否应该不同
function RouterFactory()
return setmetatable({},mt_for_router)
end
Modem = {};
mt_for_modem = {__index=Modem}
- - Modem继承自ElectronicDevice
Modem = setmetatable({},{__index=ElectronicDevice});
- - 子类的构造函数,不确定元表是否应该不同
function ModemFactory()
return setmetatable({},mt_for_modem)
end
function Modem:WarDialNeighborhood(areaCode)
cisco = RouterFactory();
- - 多态
cisco.Reboot(); - -在路由器上调用重新启动
self.Reboot(); - -在调制解调器上调用重新启动
if(self.isOn)then self:StartDialing(areaCode)end;
end
function Modem:StartDialing(areaCode)
print('正在拨打所有'..areaCode..'的号码');
end
testDevice = ElectronicDeviceFactory();
print(“设备是否打开?”..(testDevice:getIsOn()and“是”或“否”));
testDevice:Reboot(); - -好的
testRouter = RouterFactory();
testRouter:ResetHardware(); - -零价值
testModem = ModemFactory();
testModem:StartDialing('123'); - -无价值
原文链接 https://stackoverflow.com/questions/1092832
在 Lua 中实现类似 OOP 的类非常简单;只需将所有的“方法”放在元表 __index
字段中:
local myClassMethods = {}
local my_mt = {__index=myClassMethods}
function myClassMethods:func1 (x, y)
-- Do anything
self.x = x + y
self.y = y - x
end
............
function myClass ()
return setmetatable ({x=0,y=0}, my_mt)
就我个人而言,我从未需要过继承,所以上面的代码已经足够。如果不够用,你可以为方法表设置元表:
local mySubClassMethods = setmetatable ({}, {__index=myClassMethods})
local my_mt = {__index=mySubClassMethods}
function mySubClassMethods:func2 (....)
-- Whatever
end
function mySubClass ()
return setmetatable ({....}, my_mt)
更新: 你的更新代码存在错误:
Router = {};
mt_for_router = {__index=Router}
--Router inherits from ElectronicDevice
Router = setmetatable({},{__index=ElectronicDevice});
请注意,你初始化了 Router
,并从此构建了 mt_for_router
;但是你又将 Router
重新赋值为一个新表,而 mt_for_router
仍然指向原来的 Router
。
在 mt_for_router
初始化之前,将 Router={}
替换为 Router = setmetatable({},{__index=ElectronicDevice})
。
我喜欢使用实现 clone() 函数的方式来做。
请注意这是针对 Lua 5.0。我认为 5.1 有更多内置的面向对象构造。
clone = function(object, ...)
local ret = {}
-- 克隆基类
if type(object) == "table" then
for k, v in pairs(object) do
if type(v) == "table" then
v = clone(v)
end
-- 不克隆函数,只继承它们
if type(v) ~= "function" then
-- 混合其他对象。
ret[k] = v
end
end
end
-- 设置元表为对象
setmetatable(ret, { __index = object })
-- 混合表
for _, class in ipairs(arg) do
for k, v in pairs(class) do
if type(v) == "table" then
v = clone(v)
end
-- 混合 v。
ret[k] = v
end
end
return ret
end
然后你可以将一个类定义为一个表:
Thing = {
a = 1,
b = 2,
foo = function(self, x)
print("total = ", self.a + self.b + x)
end
}
要实例化它或从它派生,你使用 clone(),你可以通过将它们传递到另一个表(或表)中来覆盖它们。
myThing = clone(Thing, { a = 5, b = 10 })
要调用,你使用语法:
myThing:foo(100);
这将打印:
total = 115
要派生一个子类,你基本上定义另一个原型对象:
BigThing = clone(Thing, {
-- 并覆盖一些内容。
foo = function(self, x)
print("hello");
end
}
这种方法非常简单,可能过于简单,但对我的项目效果很好。
你更新的代码很啰嗦,但应该能正常运行。但是,你有一个打字错误导致其中一个元表失效:
--Modem inherits from ElectronicDevice
Modem = setmetatable({},{__index,ElectronicDevice});
应该改为
--Modem inherits from ElectronicDevice
Modem = setmetatable({},{__index=ElectronicDevice});
现有片段将 Modem
元表设为一个数组,其中第一个元素几乎肯定为nil(__index 的常规值,除非您使用 strict.lua
或类似工具),第二个元素是ElectronicDevice
。
在了解元表方面更多内容后,Lua Wiki 的描述将变得更加清晰。有一个方法可以构建一些基础设施,使得通常的模式更容易掌握。
我还建议阅读PIL中的面向对象编程章节。您还需要重新阅读表和元表的章节。此外,我链接的是第一版的在线副本,但强烈建议购买第二版。还有一些与之相关的文章在Lua Gems书中。这本书也是值得推荐的。
以下是您代码的一个字面翻译示例,并附带一个可移动到另一个文件的有用 Class
库。
这绝不是 Class
的权威实现;可以随意定义您的对象模型。
Class = {}
function Class:new(super)
local class, metatable, properties = {}, {}, {}
class.metatable = metatable
class.properties = properties
function metatable:__index(key)
local prop = properties[key]
if prop then
return prop.get(self)
elseif class[key] ~= nil then
return class[key]
elseif super then
return super.metatable.__index(self, key)
else
return nil
end
end
function metatable:__newindex(key, value)
local prop = properties[key]
if prop then
return prop.set(self, value)
elseif super then
return super.metatable.__newindex(self, key, value)
else
rawset(self, key, value)
end
end
function class:new(...)
local obj = setmetatable({}, self.metatable)
if obj.__new then
obj:__new(...)
end
return obj
end
return class
end
ElectronicDevice = Class:new()
function ElectronicDevice:__new()
self.isOn = false
end
ElectronicDevice.properties.isOn = {}
function ElectronicDevice.properties.isOn:get()
return self._isOn
end
function ElectronicDevice.properties.isOn:set(value)
self._isOn = value
end
function ElectronicDevice:Reboot()
self._isOn = false
self:ResetHardware()
self._isOn = true
end
Router = Class:new(ElectronicDevice)
Modem = Class:new(ElectronicDevice)
function Modem:WarDialNeighborhood(areaCode)
local cisco = Router:new()
cisco:Reboot()
self:Reboot()
if self._isOn then
self:StartDialing(areaCode)
end
end
如果您坚持使用属性的 get/set 方法,则不需要 __index
和 __newindex
函数,可以只有一个 __index
表。在这种情况下,模拟继承的最简单方法是:
BaseClass = {}
BaseClass.index = {}
BaseClass.metatable = {__index = BaseClass.index}
DerivedClass = {}
DerivedClass.index = setmetatable({}, {__index = BaseClass.index})
DerivedClass.metatable = {__index = DerivedClass.index}
换句话说,派生类的 __index
表“继承”了基类的 __index
表。这有效是因为 Lua 在委托到一个 __index
表时,实际上会在其上重复查找,因此会调用 __index
表的元方法。
此外,请注意调用 obj.Method(...)
与 obj:Method(...)
的区别。obj:Method(...)
是 obj.Method(obj, ...)
的语法糖,混淆这两个调用可能会产生异常错误。
另一种简单的子类方法
local super = require("您的基类")
local newclass = setmetatable( {}, {__index = super } )
local newclass_mt = { __index = newclass }
function newclass.new(...) -- 构造函数
local self = super.new(...)
return setmetatable( self, newclass_mt )
end
即使被重写,仍可以使用超类中的函数
function newclass:dostuff(...)
super.dostuff(self,...)
-- 更多代码在这里 --
end
在将 self 传递给超类函数时,请不要忘记使用一个点。
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
有几种方法可以实现这个,但是这是我使用的方法(更新了一个继承的例子):
function newRGB(r, g, b) local rgb={ red = r; green = g; blue = b; setRed = function(self, r) self.red = r; end; setGreen = function(self, g) self.green= g; end; setBlue = function(self, b) self.blue= b; end; show = function(self) print("red=",self.red," blue=",self.blue," green=",self.green); end; } return rgb; end purple = newRGB(128, 0, 128); purple:show(); purple:setRed(180); purple:show(); ---// 这算继承吗? function newNamedRGB(name, r, g, b) local nrgb = newRGB(r, g, b); nrgb.__index = nrgb; ---// 谁是 self? nrgb.setName = function(self, n) self.name = n; end; nrgb.show = function(self) print(name,": red=",self.red," blue=",self.blue," green=",self.green); end; return nrgb; end orange = newNamedRGB("orange", 180, 180, 0); orange:show(); orange:setGreen(128); orange:show();
我没有实现私有、受保护等 虽然这是可能的。