metatable和metamethod能够用来定义Lua的默认操作, 比如默认场景下Lua是不支持两个table的+,-,*,/操作的,但如果给table定义了metatable,并且在metatable中定义了相关操作的method,那么table就可以支持这些操作了. Lua默认只有string 库中给string设置了metatable, 其它类型都没有metatable.
print(getmetatable("aasfas")) --Table
print(getmetatable({})) --nil
注意: 随着Lua版本的不断演进, 其支持的metamethod在各个版本中也不一样, 请参考其官方文档
1. 算术metamethod
- __add: +
- __sub: -
- __mul: *
- __div: /
- __unm: negation
- __pow: exponentiation
- __concat: the .. (concatenation) operation
不同metatable类型对象的操作时选择metamethod的方式如下:
if the first value has a metatable with an __add field, Lua uses this field as the metamethod, in- dependently of the second value; otherwise, if the second value has a metatable with an __add field, Lua uses this field as the metamethod; otherwise, Lua raises an error.
2. 逻辑metamethod
- __eq (equal to),
- __lt (less than),
- __le (less than or equalto)
3. lib中定义的metamethod
- __tostring
- __metatable
If you set a __metatable
field
in the metatable, getmetatable
will return the value of this field, whereas
setmetatable
will raise an error
4. 访问table的metamethod
- __index
当access table时, 会触发Lua解释器调用table 的metamethod: __index
, __index
可以是一个function, 也可以赋值为一个table.
当赋值为一个function时, 第一个参数是table 本身, 第二个参数是要访问的key
mt = {}
mt.__index = function(_, key) {
...
}
function new (o)
setmetatable(o, mt)
return o
end
当赋值为一个prototype table时, Lua解释器将重复访问prototype table中的key, 这种方式相当于实现对象的继存, 即如下:
prototype = {x=5, y=10, width=15, height=20}
mt.__index = prototype
local rect = new{x=10, y=20}
--rect.width equivalent of prototype["width"]
--rect.x 解释器先访问rect中的x, 如果没有,再访问prototype table中的x
如果只想取table中的real value
, 而不是调用__index
, 可以调用:rawget
- __newindex
当update table时, 会触发Lua解释器调用table的metamethod: __newindex
. 可以赋一个function
mt.__newindex = function(t, k, v)
...
end
采用__index
和__newindex
可以实现只读表的功能.
如果只想给table的real index
赋值,而不调用__newindex
, 可以调用:rawset
如果实现一个metatable被多个table共用?
local index = {} -- create private index
local mt = { -- create metatable
__index = function (t, k)
print("*access to element " .. tostring(k))
return t[index][k] -- access the original table
end,
__newindex = function (t, k, v)
print("*update of element " .. tostring(k) .." to " .. tostring(v))
t[index][k] = v -- update original table
end,
__pairs = function (t)
return function (t, k)
return next(t[index], k)
end, t
end
}
function track (t)
local proxy = {}
proxy[index] = t
setmetatable(proxy, mt)
return proxy
end