Lua中元方法的继承

时间:2010-11-11 15:09:54

标签: oop inheritance lua

我非常喜欢在“lua编程”16.1,16.2中描述面向对象编程:

http://www.lua.org/pil/16.1.html

http://www.lua.org/pil/16.2.html

并希望遵循这种方法。但我想更进一步:我希望有一个称为“类”的基类“类”,它应该是所有子类的基础,因为我想在那里实现一些辅助方法(如“instanceof”等) 。),但基本上它应该如书中所述:

function class:new(o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
end

现在我的问题:

我希望有一个“数字”类,它继承自“class”:

number = class:new()

我想在这个类中为运算符重载(__add,__sub等)定义元方法,如下所示:

n1 = number:new()
n2 = number:new()

print(n1 + n2)

的工作原理。这不是一个真正的问题。但现在我想要第三类“钱”,它继承自“数字”:

money = number:new{value=10,currency='EUR'}

我在这里介绍一些新的属性等。

现在我的问题是,我不能让事情发挥作用,“钱”继承了“class”和“number”的所有方法,包括在“number”中定义的所有metamethods。 / p>

我已经尝试了一些事情,比如覆盖“新”或修改元数据,但我无法让事情发挥作用,既没有放弃“钱”中的“类”方法,也没有丢失“数字”的元方法“货币”

我知道,那里有很多类的实现,但我真的很想坚持使用lua本身的最小方法。

非常感谢任何帮助!

非常感谢!

3 个答案:

答案 0 :(得分:3)

我认为您遇到的问题是由于使用类似rawget(getmetatable(obj) or {}, "__add")的内容查找运算符元方法的事实。因此,运算符不会与其他函数一起继承。

我已经取得了一些成功,让new函数复制像这样的运算符:

function class:new(o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    local m=getmetatable(self)
    if m then
        for k,v in pairs(m) do
            if not rawget(self,k) and k:match("^__") then
                self[k] = m[k]
            end
        end
    end
    return o
end

答案 1 :(得分:2)

这个问题已经得到了解答,但是让我提出我的解决方案,这是我在开发我的OOP库时遇到的问题,middleclass

在我的情况下,我开始列出所有“有用的”元方法名称:

local _metamethods = { -- all metamethods except __index
  '__add', '__call', '__concat', '__div', '__le', '__lt', '__mod', '__mul', '__pow', '__sub', '__tostring', '__unm'
} 

我使用该列表为每个创建的类添加方法。所以,实际上,我有一个所有metamethods的“默认实现”:

-- creates a subclass
function Object.subclass(theClass, name)
  ...

  local dict = theSubClass.__classDict -- classDict contains all the [meta]methods of the 
  local superDict = theSuperClass.__classDict -- same for the superclass
  ...

  for _,mmName in ipairs(_metamethods) do -- Creates the initial metamethods
    dict[mmName]= function(...) -- by default, they just 'look up' for an implememtation
      local method = superDict[mmName] -- and if none found, they throw an error
      assert( type(method)=='function', tostring(theSubClass) .. " doesn't implement metamethod '" .. mmName .. "'" )
      return method(...)
    end
  end 

诀窍是默认实现“调用”父类的实现;如果不存在,则会抛出错误。

以这种方式实现的元方法只比常规方法稍慢(2个额外的方法调用),并且使用的内存量非常小(每个类额外12个函数)。

PS:我没有包含__len元方法,因为无论如何Lua 5.1都不尊重它。

PS2:我没有包含__index__newindex,因为我必须在内部为我的课程使用它们。

答案 2 :(得分:0)

我做了类似的事情并遇到了类似的问题。这是我的基类定义:

RootObjectType = {}
RootObjectType.__index = RootObjectType
function RootObjectType.new( o )
        o = o or {}
        setmetatable( o, RootObjectType )
        o.myOid = RootObjectType.next_oid()
        return o
end

function RootObjectType.newSubclass()
        local o = {}
        o.__index = o
        setmetatable( o, RootObjectType )
        RootObjectType.copyDownMetaMethods( o, RootObjectType )
        o.baseClass = RootObjectType
        return o
end

function RootObjectType.copyDownMetaMethods( destination, source ) -- this is the code you want
        destination.__lt = source.__lt
        destination.__le = source.__le
        destination.__eq = source.__eq
        destination.__tostring = source.__tostring
end

RootObjectType.myNextOid = 0
function RootObjectType.next_oid()
        local id = RootObjectType.myNextOid
        RootObjectType.myNextOid = RootObjectType.myNextOid + 1
        return id
end

function RootObjectType:instanceOf( parentObjectType )
        if parentObjectType == nil then return nil end
        local obj = self
        --while true do
        do
                local mt = getmetatable( obj )
                if mt == parentObjectType then
                        return self
                elseif mt == nil then
                        return nil
                elseif mt == obj then
                        return nil
                else
                        obj = mt
                end
        end
        return nil
end


function RootObjectType:__lt( rhs )
        return self.myOid < rhs.myOid
end

function RootObjectType:__eq( rhs )
        return self.myOid == rhs.myOid
end

function RootObjectType:__le( rhs )
        return self.myOid <= rhs.myOid
end

function RootObjectType.assertIdentity( obj, base_type )
        if obj == nil or obj.instanceOf == nil or not obj:instanceOf( base_type ) then
                error( "Identity of object was not valid" )
        end
        return obj
end

function set_iterator( set )
        local it, state, start = pairs( set )
        return function(...) 
                local v = it(...)
                return v
        end, state, start
end