如何在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

点赞
stackoverflow用户30470
stackoverflow用户30470

有几种方法可以实现这个,但是这是我使用的方法(更新了一个继承的例子):

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();

我没有实现私有、受保护等 虽然这是可能的

2009-07-07 15:12:11
stackoverflow用户11649
stackoverflow用户11649

在 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})

2009-07-07 15:24:42
stackoverflow用户56938
stackoverflow用户56938

我喜欢使用实现 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
}

这种方法非常简单,可能过于简单,但对我的项目效果很好。

2009-07-07 18:29:51
stackoverflow用户68204
stackoverflow用户68204

你更新的代码很啰嗦,但应该能正常运行。但是,你有一个打字错误导致其中一个元表失效:

--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书中。这本书也是值得推荐的。

2009-07-07 20:20:43
stackoverflow用户5696
stackoverflow用户5696

以下是您代码的一个字面翻译示例,并附带一个可移动到另一个文件的有用 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, ...) 的语法糖,混淆这两个调用可能会产生异常错误。

2009-07-07 23:06:02
stackoverflow用户67432
stackoverflow用户67432

如果你不想重复造轮子,一款名为 LOOP 的 Lua 库实现了几种对象模型。

2009-07-08 10:33:55
stackoverflow用户1955088
stackoverflow用户1955088

另一种简单的子类方法

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 传递给超类函数时,请不要忘记使用一个点。

2014-06-02 11:09:36