最近做開(kāi)發(fā)的時(shí)候,有個(gè)類想要繼承自多個(gè)類,雖然這種情況基本說(shuō)很少(好像也不少),但還是說(shuō)一下如何在lua里實(shí)現(xiàn)多繼承。
其實(shí)很簡(jiǎn)單,首先要了解lua元表,以及l(fā)ua類的實(shí)現(xiàn)方法。
lua元表
用lua的人都知道lua的table的強(qiáng)大,table有一套hashmap的查找機(jī)制,如果訪問(wèn)一個(gè)表中并不存在的字段,不會(huì)立即返回nil,而是先觸發(fā)一套查找機(jī)制,也是我們用以實(shí)現(xiàn)面向?qū)ο蟮姆椒?/code>。
簡(jiǎn)單的描述:元表就是用于查找的備用表。
比如:
local me = {}
print(me.money) -- me中沒(méi)有money字段,所以打印出來(lái)的會(huì)是nil
這里的執(zhí)行結(jié)果很明顯會(huì)是nil,沒(méi)有疑問(wèn)(因?yàn)槲沂莻€(gè)窮逼。
但是如果我設(shè)置了元表:
local fathermayun = {
money = 13000000000
}
local me = {}
setmetatable(me, fathermayun)
print(me.money)
這里的執(zhí)行結(jié)果是!!!!!!!
nil
為什么我沒(méi)有拿到馬云爸爸的錢?因?yàn)闆](méi)有設(shè)置__index
__index
簡(jiǎn)單的描述:是當(dāng)table中一個(gè)元素不存在的時(shí)候,會(huì)觸發(fā)尋找元表的__index元方法,如果不存在,則返回nil,如果存在,則返回結(jié)果。
所以,把上述代碼改成
local fathermayun = {
money = 13000000000
}
fathermayun.__index = fathermayun
local me = {}
setmetatable(me, fathermayun)
print(me.money)
這里的執(zhí)行結(jié)果將是13000000000,我終于有錢了:)
上面的執(zhí)行過(guò)程是:
訪問(wèn)me.money->發(fā)現(xiàn)沒(méi)有這個(gè)字段->me有元表->查找元表fathermayun->lua不會(huì)直接找fathermayun里的money字段->lua發(fā)現(xiàn)fathermayun有元方法__index->調(diào)用元方法->發(fā)現(xiàn)元方法是個(gè)table,在元方法的table里尋找->獲得money的值
__index有以下取值
表,會(huì)直接在表里找
函數(shù),返回函數(shù)的返回值
繼承
利用上述知識(shí)隨手寫(xiě)了個(gè)實(shí)現(xiàn)面向?qū)ο蟮姆椒ǎ皇呛芡晟?/p>
local function class(super)
local cls
if super then
cls = {}
cls.super = super
setmetatable(cls, {__index = super})
else
-- ctor是構(gòu)造函數(shù)的命名
cls = {ctor = function () end}
end
cls.__index = cls
function cls.new(...)
local instance = setmetatable({}, cls)
instance:ctor(...)
return instance
end
return cls
end
挺簡(jiǎn)單的,注釋就不寫(xiě)了……讓我們來(lái)測(cè)試一下
local Test = class()
function Test:doSomething()
print("test doSomething")
end
local test = Test.new()
test:doSomething()
成功打印出"test doSomething"
接下來(lái)試試?yán)^承
local Test = class()
function Test:doSomething()
print("test doSomething")
end
local Test2 = class(Test)
local test = Test2.new()
test:doSomething()
也可以成功打印出"test doSomething",繼承成功
為什么能繼承成功?原因如下:
在new的時(shí)候,創(chuàng)建一個(gè)table并返回,即創(chuàng)建一個(gè)實(shí)例,實(shí)例可以有自己的字段,比如Test類的doSomething,該字段是個(gè)函數(shù),可以調(diào)用執(zhí)行。實(shí)例的元表是cls,如果調(diào)用實(shí)例沒(méi)有的字段,會(huì)去cls里找
cls設(shè)置了元方法__index = cls
如果沒(méi)有super,則只有一個(gè)構(gòu)造函數(shù)方法
如果有super,cls的元表是super,元表的元方法也正確的設(shè)置了
所以,在Test2是繼承自Test的,它的實(shí)例test調(diào)用doSomething,找不到,去元表里找,元表發(fā)現(xiàn)自己有父類,去父類里找,成功找到。
多繼承
如果我想要繼承多個(gè)父類,怎么辦?
其實(shí)思路很簡(jiǎn)單,把元方法改成函數(shù)即可。
舉例子:
local function search(key, tables)
for _, super in ipairs(tables) do
if super[key] then
return super[key]
end
end
return nil
end
local function class(...)
local cls = { ctor = function () end}
local supers = {...}
setmetatable(cls, {__index = function (_, key)
-- 在查找table的時(shí)候,會(huì)把table的key傳進(jìn)來(lái)
return search(key, supers)
end})
function cls.new(...)
local instance = {}
setmetatable(instance, {__index = cls})
instance:ctor(...)
return instance
end
return cls
end
local Human = class()
function Human:life()
print("almost 100 years.")
end
local Programmer = class()
function Programmer:coding()
print("sub 1 year.")
end
local My = class(Human, Programmer)
local yuzixin = My.new()
yuzixin:life()
yuzixin:coding()
成功打印出結(jié)果
almost 100 years.
sub 1 year.
為什么能繼承成功?原因如下:
在yuzixin里找不到life和coding字段,去找元表cls,調(diào)用元方法__index
__index調(diào)用函數(shù)search,把所有的父類都找一遍
成功找到
多繼承就這樣成功啦!撒花!
注意寫(xiě)一次代碼減少一年的生命哦……(悲傷doge臉