用继承的思想理解lua元表
众所周知,lua最最核心的数据结构是table,是一个key-value hash表,可以用t.key或t[key]来查询。当key值不存在时,一般的hash表返回空值,但lua的table在一定条件下会触发元方法,在设置的元表table里继续查找,如果查到了,则返回元表里的值而不是空值。
跟继承类似:
A继承B,A:XXX(),如果A中没有此方法,则继续在B中查找
设置A的元表是B,A.key,如果A中没有key,在一定条件下到B中查找
1. lua查找元素的过程
local mt = {a = "mt"} mt.__index = mt local t = setmetatable({}, mt) print("---------> ", t.a) ---> mt
t.a,在表t中查找"a",不存在,如果t设置了元表,并且元表设置了__index域,则在元表设置的__index域mt中继续查找,即返回mt。如果没有设置元表返回nil。如果元表没有设置__index域也返回nil。如果在mt没找到,继续在mt的元表中继续查找,以此类推
local mt1 = {a = "mt1"} mt1.__index = mt1 local mt2 = setmetatable({}, mt1) mt2.__index = mt2 local mt = setmetatable({}, mt2) print(mt.a, mt2.a, mt1.a) ----> mt1 mt1 mt1
2. lua实现类
lua中并没有类的概念,但根据元表的特性很容易实现一个类。即设置obj表的元表为class表。有很多不同的实现思路,以下是一个常用的模板:
local class = {} class.__index = class function class.New(cls, name) local self = setmetatable({}, cls) self.name = name return self end function class:print() print("name = ", self.name) end local obj = class:New("abc") obj:print() ---> name = abc
3.lua实现继承
利用元表的特性,设置子类的元表为父类,当在子类找不到时,在父类中继续查找。
local base = {} base.__index = base function base.New(cls, name) local self = setmetatable({}, cls) self.name = name return self end function base:print() print("name = ", self.name) end local class = setmetatable({}, base) class.__index = class function class.New(cls, name, age) local self = base.New(cls, name) self.age = age return self end function class:print2() self:print() print("age = ", self.age) end local obj = class:New("abc", 10) obj:print2() ---> name = abc age =10
4. lua的元表和元方法
lua的元表和元方法自定义一系列的操作集合,除了查找元素__index域外,还有__newindex, __gc, __tostring, __add等操作,详见http://cloudwu.github.io/lua53doc/manual.html
local mt = { __tostring = function(t) local s="" for _, v in ipairs(t) do s = s .. v .. "," end return s end, __add = function(t1, t2) local t = {} for i, v in ipairs(t1) do t[i] = v end for i, v in ipairs(t2) do t[i] = (t[i] or 0) + v end return t end, } local t = setmetatable({1,2,3,4,5}, mt) print(t) ---> 1,2,3,4,5 local t1 = setmetatable({1,2,3}, mt) local t2 = setmetatable({10,20,30,40,50}, mt) local t3 = setmetatable(t1+t2, mt) print(t3) ---> 11,22,33,40,50
注:如果用rawget(t, key),rawset(t, key, value),是不会触发元方法的,只在t中操作