之前接触lua时看到了元表的使用,但是一直一知半解,借此机会对自己的理解做一下总结,方便日后回顾
元表本质上来说是一种用来存放元方法的table。我们可以通过对应的key来得到value值,作用就是修改一个值的行为(更确切的说,这是元方法的能力),需要注意的是,这种修改会覆盖掉原本该值可能存在的相应的预定义行为。
1. lua中的每个值都可以有一个元表,只是table和userdata可以有各自独立的元表,而其他类型的值则共享其类型所属的单一元表。
lua代码中只能设置table的元表,至于其他类型值的元表只能通过C代码设置。
多个table可以共享一个通用的元表,但是每个table只能拥有一个元表。
我们称元表中的键为事件(event),称值为元方法(metamethod)。前述例子中的事件是"add",元方法是执行加法的函数。
可通过函数getmetatable查询任何值的元表。
可通过函数setmetatable替换表的元表
lua查找表中的元素时规则如下:
1.在表中查找,如果找到,返回该元素,找不到则继续
2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续
3.判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值
例如下面代码
father = {
house=1
}
son = {
car=1
}
setmetatable(son, father) --把son的metatable设置为father
print(son.house)
返回为nil,如果改一下
father = {
house=1
}
son = {
car=1
}
setmetatable(son, father) --把son的metatable设置为father
father.__index = father
print(son.house)
返回为1,这是因为在son表中没有house字段,虽然father是son的元表,但是father没有元方法,即元方法没有指向father自己,因此找不到house字段,这也就解释通了为什么会经常在lua中这么写了
到这里,我们对元表有了最基本的认识,它更像是一个备用的查找表,如果a的元表是b,在a中查找不到的东西会尝试从b中继续找,当然,前提是b设置了元方法
__index元方法:
上面我们说道在b中查找a中不存在的值时,需要给b的元方法赋值,不然仍然会像上面的例子返回为nil,因此,元方法在这里像是一个a打开b表查找入口的钥匙,我们顺着上面的逻辑再理一下整个查找思路:
我们在访问son.house时,son中没有house这个成员,但Lua接着发现son有元表father,于是此时father被当做元表来查找,此时,Lua并不是直接在father中找名为house的成员,而是调用father的__index方法,如果__index方法为nil,则返回nil,如果是一个表(上例中father的__index方法等于自己,就是这种情况),那么就到__index方法所指的这个表中查找名为house的成员,于是,最终找到了house成员。
注:__index方法除了可以是一个表,还可以是一个函数,如果是一个函数,__index方法被调用时将返回该函数的返回值
下面引用一个菜鸟教程的例子,很能说明问题
对指定的表设置元表:
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
以上代码也可以直接写成一行:
mytable = setmetatable({},{})
以下为返回对象元表:
getmetatable(mytable) -- 这回返回mymetatable
如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果。
mytable = setmetatable({key1 = "value1"}, {
__index = function(mytable, key)
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
})
print(mytable.key1,mytable.key2)
实例输出结果为: value1 metatablevalue
实例解析:
- mytable 表赋值为 {key1 = "value1"}。
- mytable 设置了元表,元方法为 __index。
- 在mytable表中查找 key1,如果找到,返回该元素,找不到则继续。
- 在mytable表中查找 key2,如果找到,返回 metatablevalue,找不到则继续。
- 判断元表有没有__index方法,如果__index方法是一个函数,则调用该函数。
- 元方法中查看是否传入 "key2" 键的参数(mytable.key2已设置),如果传入 "key2" 参数返回 "metatablevalue",否则返回 mytable 对应的键值。
我们可以将以上代码简单写成:
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)