c++和java語言機制中自己帶有面向對象的內容,而lua設計的思想是元編程,沒有面向對象的實現。java
可是利用lua的元表(matetable)機制,能夠實現面向對象。要講清楚怎樣實現lua面向對象,須要講清楚如下內容。c++
1.lua元表 2.類和對象 3.繼承 編程
1.lua元表數據結構
lua裏的全部數據結構都是表。metatable能夠改變table的行爲。例如加法行爲,table自己沒有加法行爲。lua
能夠經過修改元表中的__add域,來提供表的加法行爲。__add叫作元方法(metamethod)。spa
一個表默認是不帶元表的。getmetatable能夠獲取元表。設計
t = {} print(getmetatable(t)) --nil
setmetatable能夠設置元表。code
t = {} setmetatable(t, t) --設置本身是本身的元表 print(t == getmetatable(t)) --true
任何一個表能夠其餘表的元表。一個表也能夠是本身的元表。對象
來看一個展現__add元方法的例子。實現一個集合的並集運算。blog
Set = {} Set.mt = {} Set.new = function (t) local res = {} setmetatable(res, Set.mt) for k, v in pairs(t) do --用原始集合的value做爲新集合的key res[v] = true --新集合的value值 能夠是任意值 end return res end Set.union = function (a, b) local res = Set.new{} --res將Set.mt做爲元表 for k, v in pairs(a) do res[k] = true end --res的全部key值做爲新的並集,value值能夠是任意值 for k, v in pairs(b) do res[k] = true end return res end Set.mt.__add = Set.union Set.print = function (t) local s = "{ " for k, v in pairs(t) do s = s .. tostring(k) .. " " --全部key值做爲新的並集 end s = s .. "}" print(s) end s1 = Set.new{1, 2, 3, "hello"} s2 = Set.new{2, 3, 4, "world"} s3 = s1 + s2 Set.print(s3) --{ 1 2 3 4 hello world}
在new集合時將一個公共的mt表做爲了集合的元表,那麼經過new建立的集合都會有相同的元表,也就有了相同的行爲。
再經過定義__add域,則改變了表的「+」行爲,使得「+」變成了集合的並集運算。
能夠重定義的元方法以下:
__add --加法 __sub --劍法 __mul --乘法 __div --除法 __unm --負 __pow --冪 __concat --鏈接 __eq --等於 __lt --小於 __le --小於等於 __tostring --字符串輸出 __index --訪問表的域 __newindex --更新表的域 __metatable --使元表不能被修改
其中最重要的__index,__newindex是用來實現面向對象的關鍵。下面一個對錶的監控例子,能夠看出__index,__newindex的做用。
original = {} --原始表 mt = {} mt.__index = function (t, k) --此表的訪問操做,都會訪問original表 print("access table element " .. tostring(k) .. " : " .. tostring(original[k])) return original[k] end mt.__newindex = function (t, k, v) --此表的賦值操做,都會操做original表 print("update table element " .. tostring(k) .. " : " .. tostring(original[k]) .. " to " .. tostring(v)) original[k] = v end t = {} --監控表 用來監控original setmetatable(t, mt) t[1] = "hello" --update table element 1 : nil to hello str = t[1] .. "world" --access table element 1 : hello print(str) --helloworld
當咱們訪問一個表的不存在的域時,會觸發訪問__index方法。當表缺乏一個賦值域時,會觸發訪問__newindex方法。
這兩個重要的元方法是實現面向對象的關鍵。
2.類和對象
咱們建立一個對象做爲其餘對象的原型,當調用不屬於該對象的方法時,會去原型中查找。
setmetatable(a, {__index = b})
則b是原型,a是原型的對象。概念上稱b是類,a是b的實例對象。調用a中不存在的對象時,會去b中查找。
Cal = {} function Cal:New(o) o = o or {} setmetatable(o, self) self.__index = self return o end function Cal:Add(a, b) print("Cal Add") return a + b end a = Cal:New() print(a:Add(5, 6)) --11
調用Cal:New實際會返回一個以Cal本身爲元表的新對象。而a中沒有Add方法,則會在Cal中查找Add方法。
3.繼承
有了類與對象,咱們須要在此基礎上實現繼承。
Cal = {} function Cal:New(o) o = o or {} setmetatable(o, self) self.__index = self return o end function Cal:Add(a, b) print("Cal Add") return a + b end SubCal = Cal:New() function SubCal:Add(a, b) print("SubCal Add") return a + b end a = SubCal:New() print(a:Add(5, 6)) --11
一個SubCal子類,從基類Cal中繼承了Add方法。a對象會首先在SubCal中查詢方法,若是有則調用,所以子類能夠複寫父類的方法。若是沒有則去到父類中查找方法。