在Lua中,元方法的继承

我非常喜欢“Lua编程”16.1, 16.2中描述面向对象编程的方式:http://www.lua.org/pil/16.1.htmlhttp://www.lua.org/pil/16.2.html,并且想要遵循这个方法。但是我想进一步发展一下:我想要一个名为“class”的基础“类”,它应该是所有子类的基础,因为我想在那里实现一些帮助方法(比如“instanceof”等),但本质上应该像书中描述的那样:

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

现在我的问题是:

我想要一个从“class”继承的“number”类:

number = class:new()

我想要在这个类中定义运算符重载的元方法(例如 __add,__sub等),所以下面的代码段将能够正常工作:

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

print(n1 + n2)

这不是个问题。但是,现在我想要一个第三个类“money”,它从“number”继承:

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

我在这里引入了一些新属性等。

我的问题是,我不能让“money”继承“class”和“number”中定义的所有元方法,包括“number”中定义的所有元方法。

我尝试了几种方法,例如重写“new”或修改元表,但我无法让他们正常工作,或者在“money”中丢失“class”的方法或在“money”中丢失“number”的元方法。

我知道有很多类的实现方法,但我真的很想坚持Lua本身的最简方法。

非常感谢您的帮助!

原文链接 https://stackoverflow.com/questions/4155842

点赞
stackoverflow用户173806
stackoverflow用户173806

我认为你遇到的问题是由于操作符元方法是使用类似于 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
2010-11-11 18:49:08
stackoverflow用户156521
stackoverflow用户156521

我做了类似的事情并且遇到了相似的问题。下面是我的基类定义:

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 ) -- 这是你需要的代码
        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
2010-11-11 19:04:13
stackoverflow用户312586
stackoverflow用户312586

这个问题已经有答案了,但是让我来介绍一下我遇到的这个问题时的解决方案,当时我正在开发我的 OOP lib middleclass

对于这个问题,我开始列出了所有 "有用" 的元方法名称的列表:

local _metamethods = { -- 所有的元方法除了 __index
  '__add', '__call', '__concat', '__div', '__le', '__lt', '__mod', '__mul', '__pow', '__sub', '__tostring', '__unm'
}

我使用这个列表来为创建的每个类添加方法。因此,实际上,我有了所有元方法的 "默认实现":

-- 创建一个子类
function Object.subclass(theClass, name)
  ...

  local dict = theSubClass.__classDict -- classDict 包含所有的[元]方法
  local superDict = theSuperClass.__classDict -- 超类也是如此
  ...

  for _,mmName in ipairs(_metamethods) do -- 创建初始的元方法
    dict[mmName]= function(...) -- 默认情况下,它们只是去查找一个实现
      local method = superDict[mmName] -- 如果没有找到,它们就抛出一个错误
      assert( type(method)=='function', tostring(theSubClass) .. " doesn't implement metamethod '" .. mmName .. "'" )
      return method(...)
    end
  end

诀窍就在于默认实现 "调用" 父类的实现;如果不存在,它会抛出一个错误。

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

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

附2:我也没有包括 __index__newindex,因为我必须在我的类内部使用它们。

2011-03-24 13:46:22