简介
Lua中的元表允许我们通过一个tableA特殊的键值自定义另一个tableB的行为,这些特殊的键值称为元方法,tableA则称为tableB的元表。
设置元表
- setmetatable(table,metatable): 对指定 table 设置元表,如果元表中存在 __metatable 键值,setmetatable 会失败。
- getmetatable(table): 返回对象的元表。
元方法
函数 | 描述 |
__index | 调用一个索引 |
__newindex | 给一个索引赋值 |
__add | 运算符 + |
__sub | 运算符 - |
__mul | 运算符 * |
__ div | 运算符 / |
__mod | 运算符 % |
__unm | 运算符 -(取反) |
__concat | 运算符 .. |
__eq | 运算符 == |
__lt | 运算符 < |
__le | 运算符 <= |
__call | 当函数调用 |
__tostring | 转化为字符串 |
__index
__index用于对表访问。
当通过一个key来访问 tableA 的时候,如果这个key没有对应的value,即为nil,那么Lua就会寻找tableA的元表中的__index 键。
如果__index是一个tableB,Lua会在tableB查找相应的value,如果不存在则返回nil,存在则返回对应的value;
如果__index是一个函数,Lua就会调用那个函数,tableA和key会作为参数传递给函数。
tableA = {a = "valueA"}
tableB = {b = "valueB"}
setmetatable(tableA, {__index = tableB})
print(tableA.b) -- 输出结果:valueB
setmetatable(tableA, {__index = function (t, k)
print(t.a) -- 输出结果:valueA
return "return funcion"
end})
print(tableA.b) -- 输出结果:return funcion
__newindex
__newindex 用于对表更新。
当给tableA一个不存在的key赋值时,Lua就会查找元表中的__newindex键。
如果__newindex是一个tableB,则该key和value赋值到tableB中,不对tabelA进行改变;
如果__newindex是一个函数,则将赋值语句中的tableA、key、value当作参数去调用该函数,不对tabelA进行改变。
tableA = {a = "valueA"}
tableB = {}
-- 如果__newindex是个table
setmetatable(tableA, {__newindex = tableB})
tableA.b = "valueB"
print(tableA.b) -- 输出结果:nil
print(tableB.b) -- 输出结果:valueB
tableA = {a = "valueA"}
tableB = {}
-- 如果__newindex是个function
setmetatable(tableA, {__newindex = function (t, k, v)
print(t.a) -- 输出结果:valueA
print(k) -- 输出结果:b
print(v) -- 输出结果:valueB
end})
tableA.b = "valueB"
print(tableA.b) -- 输出结果:nil
print(tableB.b) -- 输出结果:nil
__add
__add可以定义两个table之间的加法运算,类似于c语言中的运算符重载。具体步骤如下,
- 查看tableA是否有元表,若有,则查看tableA的元表是否有__add元方法,若有则以tableA和tableB作为参数调用;
- 查看tableB是否有元表,若有,则查看tableB的元表是否有__add元方法,若有则以tableA和tableB作为参数调用;
- 若tableA和tableB没有元表或__add则会报错。
tableA = {a = 1}
tableB = {b = 2}
setmetatable(tableA, {__add = function (t1, t2)
return t1.a + t2.b
end})
print(tableA + tableB) -- 输出结果:3
其余运算元方法的运用均类似于__add。
__call
__call可以将table封装成一个函数来调用,第一个参数是table本身,其余参数可以自定义。
table = {}
setmetatable(table, {__call = function (t, value)
return value + 1
end})
print(table(3)) -- 输出结果:3
__tostring
__tostring可以自定义table的输出形式。
table = {}
setmetatable(table, {__tostring = function (t, value)
return "I am a table."
end})
print(table) -- 输出结果:I am a table.
rawget、rawset
若想直接改动或获取table中的value时,可以分别使用rawget和rawset。
rawget可以直接获取到表中索引的实际值,而不通过元表的__index元方法。
function rawget(table, key) end
tableA = {}
tableB = {b = "valueB"}
setmetatable(tableA, {__index = tableB})
print(tableA.b) -- 输出结果:value
--通过rawget直接获取tableA中的b索引
print(rawget(tableA,"b")) -- 输出结果:nil
rawset可以直接为table中索引的赋值,而不通过元表的__newindex元方法。
function rawset(table, key, value) end
tableA = {a = "valueA"}
tableB = {}
-- 如果__newindex是个table
setmetatable(tableA, {__newindex = tableB})
-- tableA.b = "valueB" -- 会访问到__newindex
rawset(tableA, "b", "valueB") -- 避开__newindex
print(tableA.b) -- 输出结果:valueB
print(tableB.b) -- 输出结果:nil
参考文献:
https://www.runoob.com/lua/lua-metatables.html
https://www.cnblogs.com/blueberryzzz/p/8947446.html
https://blog.csdn.net/xocoder/article/details/9028347