Appearance
元表(Metatable)
元表(Metatable)是 Lua 中一个非常强大的特性,它允许我们自定义表的行为。本章节将介绍 Lua 中元表的概念、使用方法和常见应用。
什么是元表
元表是一个表,它定义了另一个表的行为。当 Lua 对一个表进行某些操作时,如果该表有元表,Lua 会查找元表中对应的方法来执行操作。
元表的设置
使用 setmetatable 函数设置表的元表:
lua
local t = {}
local mt = {}
setmetatable(t, mt)使用 getmetatable 函数获取表的元表:
lua
local t = {}
local mt = {}
setmetatable(t, mt)
print(getmetatable(t) == mt) -- 输出 true元方法
元表中的键称为元方法(Metamethod),它们定义了表的特殊行为。以下是一些常见的元方法:
__index 元方法
当访问表中不存在的字段时,Lua 会调用 __index 元方法:
lua
local mt = {
__index = function(t, key)
return "默认值"
end
}
local t = {a = 1, b = 2}
setmetatable(t, mt)
print(t.a) -- 输出 1
print(t.c) -- 输出 默认值__index 也可以是一个表,当访问表中不存在的字段时,Lua 会在 __index 表中查找:
lua
local defaults = {c = 3, d = 4}
local mt = {
__index = defaults
}
local t = {a = 1, b = 2}
setmetatable(t, mt)
print(t.a) -- 输出 1
print(t.c) -- 输出 3
print(t.d) -- 输出 4__newindex 元方法
当给表中不存在的字段赋值时,Lua 会调用 __newindex 元方法:
lua
local mt = {
__newindex = function(t, key, value)
print("赋值:" .. key .. " = " .. value)
rawset(t, key, value)
end
}
local t = {a = 1, b = 2}
setmetatable(t, mt)
t.c = 3 -- 输出 赋值:c = 3
print(t.c) -- 输出 3__add 元方法
当对表进行加法操作时,Lua 会调用 __add 元方法:
lua
local mt = {
__add = function(t1, t2)
local result = {}
for k, v in pairs(t1) do
result[k] = v
end
for k, v in pairs(t2) do
result[k] = v
end
return result
end
}
local t1 = {a = 1, b = 2}
local t2 = {c = 3, d = 4}
setmetatable(t1, mt)
setmetatable(t2, mt)
local t3 = t1 + t2
print(t3.a, t3.b, t3.c, t3.d) -- 输出 1 2 3 4__call 元方法
当将表作为函数调用时,Lua 会调用 __call 元方法:
lua
local mt = {
__call = function(t, ...)
print("调用表:", ...)
return "返回值"
end
}
local t = {}
setmetatable(t, mt)
local result = t(1, 2, 3) -- 输出 调用表: 1 2 3
print(result) -- 输出 返回值__tostring 元方法
当使用 tostring 函数或 print 函数输出表时,Lua 会调用 __tostring 元方法:
lua
local mt = {
__tostring = function(t)
local str = "{"
local first = true
for k, v in pairs(t) do
if not first then
str = str .. ", "
end
str = str .. k .. " = " .. v
first = false
end
str = str .. "}"
return str
end
}
local t = {a = 1, b = 2, c = 3}
setmetatable(t, mt)
print(t) -- 输出 {a = 1, b = 2, c = 3}其他元方法
| 元方法 | 描述 |
|---|---|
| __sub | 减法操作 |
| __mul | 乘法操作 |
| __div | 除法操作 |
| __mod | 取模操作 |
| __pow | 幂运算操作 |
| __unm | 一元负号操作 |
| __concat | 字符串连接操作 |
| __eq | 等于比较操作 |
| __lt | 小于比较操作 |
| __le | 小于等于比较操作 |
| __len | 长度操作 |
| __gc | 垃圾回收操作 |
元表的应用
示例 1:实现只读表
lua
function readonly(t)
local mt = {
__index = t,
__newindex = function(t, key, value)
error("试图修改只读表")
end
}
return setmetatable({}, mt)
end
local original = {a = 1, b = 2}
local readOnly = readonly(original)
print(readOnly.a) -- 输出 1
readOnly.c = 3 -- 报错:试图修改只读表示例 2:实现面向对象编程
lua
local Person = {}
Person.__index = Person
function Person:new(name, age)
local obj = {}
setmetatable(obj, self)
obj.name = name
obj.age = age
return obj
end
function Person:sayHello()
print("Hello, my name is " .. self.name)
end
local person = Person:new("张三", 30)
person:sayHello() -- 输出 Hello, my name is 张三示例 3:实现单例模式
lua
local Singleton = {}
local instance
function Singleton:new()
if not instance then
instance = {}
setmetatable(instance, self)
self.__index = self
end
return instance
end
local s1 = Singleton:new()
local s2 = Singleton:new()
print(s1 == s2) -- 输出 true示例 4:实现代理模式
lua
function proxy(t)
local mt = {
__index = function(_, key)
return t[key]
end,
__newindex = function(_, key, value)
t[key] = value
end,
__pairs = function()
return pairs(t)
end,
__ipairs = function()
return ipairs(t)
end,
__len = function()
return #t
end
}
return setmetatable({}, mt)
end
local original = {1, 2, 3}
local p = proxy(original)
print(p[1]) -- 输出 1
p[4] = 4
print(#p) -- 输出 4
for i, v in ipairs(p) do
print(v)
end元表的继承
元表可以实现继承:
lua
local Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {}
setmetatable(obj, self)
obj.name = name
return obj
end
function Animal:speak()
print("动物发出声音")
end
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, Animal)
function Dog:new(name)
return Animal.new(self, name)
end
function Dog:speak()
print("汪汪汪")
end
local dog = Dog:new("旺财")
dog:speak() -- 输出 汪汪汪小结
本章节介绍了 Lua 中元表的概念、设置方法、元方法和常见应用,包括 __index、__newindex、__add、__call、__tostring 等元方法,以及元表在实现只读表、面向对象编程、单例模式和代理模式等方面的应用。掌握元表的使用,对于编写 Lua 程序非常重要。