多重继承(Multiple Inheritance)

简介: 多重继承(Multiple Inheritance)

由于 Lua 语言中的对象不是基本类型,因此在 Lua 语言中进行面向对象编程时有几种方式。我们通过使用 __index 元方法的做法实现了类的继承功能,这种做法可能是在简易、性能和灵活性方面最均衡的做法。不过尽管如此,还有一些其他的实现对某些特殊的情况下可能加合适。


多重继承的实现在于把一个函数用作 __index 元方法。


提示

当一个表的元表中的 __index 字段为一个函数时,当 Lua 不能在原来的表中找到一个键时就会调用这个函数。


基于此,就可以让 __index 元方法在其他期望的任意数量的父类中查找缺失的键。


多重继承意味着一个类可以具有多个超类。因此,我们不应该使用一个(超)类中的方法来创建子类,而是应该定义一个独立的函数 createClass 来创建子类。函数 createClass 的参数为新类的所有超类,该函数创建一个表来表示新类,然后设置新类元表中的元方法 __index ,由元方法实现多重继承。虽然是多重继承,但每个实例仍属于单个类,并在其中查找所有的方法。因此,类和超类之间的关系不同于类和实例之间的关系。尤其是,一个类不能同时成为其实例和子类的元表。我们将类保存为其实例的元表,并创建了另一个表作为类的元表。如下所示:

-- 在表'plist'的列表中查找'k'
local function search (k, plist)
  for i = 1, #plist do
    local v = plist[i][k]   -- 尝试第'i'个超类
    if v then return v end
  end
end
function createClass (...)
  local c = {}                -- 新类
  local parents = {...}       -- 父类列表
  -- 在父类列表中查找类缺失的方法
  setmetatable(c, {
    __index = function (t, k)
      return search(k, parents)
    end
  })
  -- 将'c'作为其实例的元表
  c.__index = c
  -- 为新类定义一个新的构造函数
  function c:new (o)
    o = o or {}
    setmetatable(o, c)
  end
  return c  -- 返回新类
end


让我们用一个简单的示例来演示 createClass 的用法。假设前面提到的类 Account 和另一个只有两个方法 setnamegetname 的类 Named

Named = {}
function Named:getname()
  return self.name
end
function Named:setname(n)
  self.name = n
end


要创建一个同时继承 AccountNamed 的新类 NamedAccount ,只需要调用 createClass 即可,如下所示:

NamedAccount = createClass(Account, Named)


可以像平时一样创建和使用实例:

account = NamedAccount:new{name = "Paul"}
print(account:getname())      -- Paul


现在,让我们来学习 Lua 语言是如何对表达式 account:getname() 求值的;更确切的说,让我们来学习 account["getname"] 的求值过程。


首先, Lua 语言在 account 中找不到字段 "getname" ;因此,它就查找 account 的元表中的 __index 字段,在我们的示例中该字段为 NamedAccount 。由于在 NamedAccount 中也不存在字段 "getname" ,所以再从 NamedAccount 的元表中查找 __index 字段。由于这个字段是一个函数,因此 Lua 语言就调用了这个函数(即 search )。该函数先在 Account 中查找 "getname" ;未找到后,继而在 Named 中查找并最终在 Named 中找到了一个非 nil 的值,也就是最终的搜索结果。


当然,由于这种搜索具有一定的复杂性,因此多重继承的性能不如单继承。一种改进性能的简单做法是将被继承的方法复制到子类中,通过这种技术,类的 __index 元方法就会变成:

setmetatable(c,{
  __index = function (t, k)
    local v = search(k, parents)
    t[k] = v    -- 保存下来用于下次访问
    return v
  end
})


使用了这种技巧后,在第一次访问过被继承的方法后,再访问被继承的方法就会像访问局部方法一样快了。这种技巧的缺点在于当系统开始运行后修改方法的定义就比较困难了,这是因为这些修改不会沿着继承层次向下传播

目录
相关文章
|
6月前
|
编译器
|
Java Scala Maven
illegal cyclic inheritance involving trait Iterable_2种解决方式
illegal cyclic inheritance involving trait Iterable_2种解决方式
260 0
|
算法 Python
如何实现Python中的多重继承(Multiple Inheritance)以及方法解析顺序(MRO)
如何实现Python中的多重继承(Multiple Inheritance)以及方法解析顺序(MRO)
278 0
继承(Inheritance)
继承(Inheritance)
95 0
typescript38-class的构造函数实例方法继承(implement)
typescript38-class的构造函数实例方法继承(implement)
89 0
typescript38-class的构造函数实例方法继承(implement)
|
Java 调度 iOS开发