Lua 提供了元表(Metatable),允许我们改变 table 的行为,每个行为关联了对应的元方法。

两个函数来处理元表:
setmetatable(table,metatable):对指定table设置元表(metatable),返回table。如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。
getmetatable(table): 返回对象的元表(metatable)。

设置元表:

mytable = {}                          -- 普通表
mymetatable = {}                      -- 元表
setmetatable(mytable,mymetatable)     -- 把 mymetatable 设为 mytable 的元表

--相当于 mytable = setmetatable({},{})

元方法

__index 元方法

__index元方法用来对表进行访问 。访问table的时候,如果没有这个key,那么会查找metatable中__index的key。

  1. 如果__index指向一个表,Lua会在表中查找相应的key。
mytable = setmetatable({key = 1},{__index = {key2 = 2}})
print(mytable.key)   --1
print(mytable.key2)  --2
  1. 如果__index指向一个函数的话,Lua就会调用那个函数,table和key会作为参数传递给函数。
local  function mymetatable(table,key)
	return "访问了table中不存在的key:"..key
end
mytable = setmetatable({key1 = "value1"}, {
  __index = mymetatable
})
print(mytable.key1)
print(mytable.key2)
__newindex 元方法

__newindex 元方法用来对表更新。更新table的时候,如果没有这个key,那么会查找metatable中__newindex元方法,如果存在则调用这个元方法而不对table进行操作。

  1. 如果__newindex指向一个表,Lua会在表中更新相应的key。
mymetatable = {}
mytable = setmetatable({key1 = "key1"}, { __newindex = mymetatable })
print(mytable.key1)--key1
mytable.newkey = "newkey"
print(mytable.newkey) -- nil
print(mymetatable.newkey) -- newkey
--mytable 中没有newkey,调用元表中的__newindex,对mymetatable更新
mytable.key1 = "newkey_1"
print(mytable.key1) --newkey_1
print(mymetatable.key1) --nil
--mytable 中有key1,直接更新mytable
  1. 如果__newindex指向一个函数的话,Lua就会调用那个函数,table,key,value会作为参数传递给函数。
    可以使用rawset函数更新表的数据:
mytable = setmetatable({key1 = "value1"}, {
    __newindex = function(mytable, key, value)
        rawset(mytable, key, "\""..value.."\"")
    end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1,mytable.key2)

创建只读table,防止table被别人修改

function ReadOnly(t)
    local proxy = {}  --定义一个空表,访问任何索引都是不存在的,所以会调用__index 和__newindex
    local mt = {
    __index = t, ---__index 可以是函数,也可以是table,是table的话,调用直接返回table的索引值
    __newindex = function(t,k,v)
        error("attempt to update a read-only table",2)
    end
    }
    setmetatable(proxy,mt)
    return proxy
end
 
days = ReadOnly{"Sunday","Monday","Tuesday","Wednessday","Thursday","Friday","Saturday"} 
print(days[1])
days[2] = "hello"                      --这一行就非法访问了
表的运算操作符元方法

运算操作符指向一个函数,当使用运算符时,lua会调用对应的元方法,并将运算符两边的表当作参数传入元方法。
__add 对应的运算符 ‘+’.
__sub 对应的运算符 ‘-’.
__mul 对应的运算符 ‘*’.
__div 对应的运算符 ‘/’.
__mod 对应的运算符 ‘%’.
__unm 对应的运算符 ‘-’.
__concat 对应的运算符 ‘…’.
__eq 对应的运算符 ‘==’.
__lt 对应的运算符 ‘<’.
__le 对应的运算符 ‘<=’.

local function addFunc(a, b)
	 for i = 1, #b do
      table.insert(a, #a+1,b[i])
    end
    return a
end
-- 两表相加操作
mytable = setmetatable({ 2, 3 }, {
  __add = addFunc
})

secondtable = {5,6}

mytable = mytable + secondtable
        for k,v in ipairs(mytable) do
print(k,v)
end
__call 元方法

当table调用一个值时会调用__call 元方法,并将table和传入的参数作为
__call 的参数。参数可以是表,值,字符串

mytable = setmetatable({10}, {
  __call = function(mytable, args)
        sum = 0
        for i = 1, #(mytable) do
                sum = sum + mytable[i]
        end
		sum = sum+ args
        return sum
  end
})
print(mytable(1))                     -- 11
__tostring 元方法

__tostring 元方法用于修改表的格式化输出字符串。

local mytable = { 10, 20, 30 }
print(mytable)                    --table: 0xb42650
mytable = setmetatable(mytable, {
  __tostring = function(mytable)
   local str = "{"
    for k, v in pairs(mytable) do
                str = str.. v ..","
        end
    return  str.."}"
  end
})
print(mytable)                      --{10,20,30,}