关于Lua的元表,建议大家先读读Programming in Lua这本书里的讲解,我这里也对书里所讲的内容作一些总结。
一、什么是元表?
元表(meta table)定义了一组方法(元方法),来改变一个表的行为。我把元表作为一个表的行为模式来理解。
基本的操作如下:
local Table = {}
local mt = getmetatable(Table)-- Lua默认是没有为表创建元表的
print(mt) -- ---> nil
二、有哪些元方法?
按书本里的内容,元方法分为下面四类:
(注意,下面的方法都是元表的方法:比如 mt 是 Table的元表,则下面的方法就是mt.__xxx,而不是Table.__xxx)
1. 第一类——算术运算相关的元方法:加减乘除。
分别是 __add、__sub、__mul、__div。
当两个表A,B作加、减、乘、除运算时,如:A + B,如果有相应的元方法,则会自动调用其中之一的元方法。哪具体调用哪一个表的元方法呢?A还是B?
这里是Lua 选择元方法的原则:
1 、如果第一操作数拥有附带__add 域的元表,Lua 都将使用该元方法,而不管第二操作数为何;
2 、如果第一操作符不满足上述条件,而第二操作数却满足该条件,Lua 使用第二操作数的元方法;
3 、除此之外,Lua 将抛出错误。
此外还有:__unm(负数操作符),__pow(指数操作符),__concat(连接操作符)
2. 第二类——关系运算相关的元方法:
分别是 __eq(等于)、__lt(小于)和__le (小于等于)。没有不等于、大于和大于等于。
这里要注意!!! 把 __le 和 __lt 的实现要分开。因为存在NaN(not a number),可能出现 a<=b, 但 b<a 二者同时成立。
3. 第三类——库预定义的元方法:
__tostring:在使用print的时候,Lua会自动去找该表(如果是一个table的话)对应的元表的__tostring。如果有,print打印出的就是该元方法的返回值。
__metatable:可以通过这个方法为Table设置一个预设元表,比如 Table.mt.__metatable = "not your business",预设后,就不能通过setmetatable来设置元表了。
4. 第四类——表存取操作的元方法:
mt.__index:当Lua 发现 表A 不存在某个域w(可理解成下标),但拥有一个附带 __index 域的元表时,Lua 将以 A(table 参数)和 w(缺少的域)作为参数调用__index 元方法,而该元方法将在原型中查找缺少的域并返回其对应的值。
__index 元方法在继承中非常常见,因此Lua 提供了一套更便捷的方式。__index 不必非得是一个函数,它也可以是一个表。当它是一个函数时,Lua 将以表和缺少的域作为参数调用这个函数;而当它是一个表的时候,Lua 将在这个表中查找是否含有缺少的域。
mt.__newindex:__newindex 元方法用于更新表,而__index 元方法则用于访问表。
当你给表的一个缺失的域赋值,解释器就会查找__newindex 元方法:如果存在,则调用它,而不进行赋值操作。像__index 一样,如果元方法是一个表,解释器将对该表进行赋值操作,而不是原来的表。另外,有一个能直接存取的函数可以绕过元方法:执行rawset(t, k, v)将不会调用任何元方法,而是直接赋值v 给t 表的k 域。