015-Lua 元表(Metatable)

在 Lua 中,元表(Metatable) 是一种强大的机制,用于改变表的默认行为。通过元表,你可以实现自定义的操作,例如运算符重载、访问控制、元方法等。


1. 元表的基本概念

  • 每个 Lua 表可以附加一个元表。
  • 元表是一个普通的 Lua 表,其中包含特定的元方法(以键值对的形式存储)。
  • 元方法是 Lua 定义的一些特殊字段,用于修改表的行为,比如加法、减法等操作符。
  • 可以通过 setmetatablegetmetatable 函数来设置和获取元表。

1.1 设置元表

  • setmetatable(table, metatable):为表设置元表。
  • getmetatable(table):获取表的元表。
示例:设置元表

local tbl = {} -- 定义一个表
local mt = {}  -- 定义一个元表

setmetatable(tbl, mt) -- 为表 tbl 设置元表 mt

print(getmetatable(tbl) == mt) -- 输出:true

1.2 元表的作用

元表允许你:

  1. 重定义表的行为
    • 运算符重载(+, -, *, / 等)。
    • 自定义索引(__index)和新值赋值(__newindex)。
  2. 实现继承:通过共享行为实现类似面向对象的设计。
  3. 控制表的访问:如只读表或动态字段。

2. 元方法

元方法是元表中定义的一些特殊字段,用于修改表的特定行为。以下是常见的元方法及其作用。


2.1 __index 元方法

  • 当访问表中不存在的键时,会调用 __index
  • __index 可以是表或函数:
    • 如果是表,会在该表中查找键。
    • 如果是函数,会调用该函数,并将表和键作为参数传入。
示例:__index 为表

local mt = {__index = {name = "Lua", version = 5.4}}

local tbl = {}
setmetatable(tbl, mt)

print(tbl.name)    -- 输出:Lua (元表中查找到的值)
print(tbl.version) -- 输出:5.4
示例:__index 为函数

local mt = {
    __index = function(table, key)
        return key .. " is not found"
    end
}

local tbl = {}
setmetatable(tbl, mt)

print(tbl.name) -- 输出:name is not found

2.2 __newindex 元方法

  • 当给表中不存在的键赋值时,会调用 __newindex
  • __newindex 可以是表或函数:
    • 如果是表,会将新值存储到该表中。
    • 如果是函数,会调用该函数,并将表、键和值作为参数传入。
示例:__newindex 为表

local mt = {__newindex = {}}

local tbl = {}
setmetatable(tbl, mt)

tbl.name = "Lua"
print(mt.__newindex.name) -- 输出:Lua (值存储到元表中)
示例:__newindex 为函数

local mt = {
    __newindex = function(table, key, value)
        print("Attempting to set " .. key .. " to " .. value)
    end
}

local tbl = {}
setmetatable(tbl, mt)

tbl.name = "Lua" -- 输出:Attempting to set name to Lua

2.3 运算符相关元方法

元表允许重载运算符。以下是常见的运算符元方法:

元方法 描述 示例运算符
__add 加法重载 a + b
__sub 减法重载 a - b
__mul 乘法重载 a * b
__div 除法重载 a / b
__mod 取模重载 a % b
__pow 幂运算重载 a ^ b
__eq 等于比较 a == b
__lt 小于比较 a < b
__le 小于等于比较 a <= b
__concat 连接操作符重载 a .. b
__unm 一元负号运算符重载 -a
示例:加法重载

local mt = {
    __add = function(a, b)
        return {value = a.value + b.value}
    end
}

local a = {value = 10}
local b = {value = 20}
setmetatable(a, mt)
setmetatable(b, mt)

local c = a + b
print(c.value) -- 输出:30
示例:比较操作符

local mt = {
    __eq = function(a, b)
        return a.value == b.value
    end,
    __lt = function(a, b)
        return a.value < b.value
    end,
    __le = function(a, b)
        return a.value <= b.value
    end
}

local x = {value = 10}
local y = {value = 20}
setmetatable(x, mt)
setmetatable(y, mt)

print(x == y) -- 输出:false
print(x < y)  -- 输出:true
print(x <= y) -- 输出:true

2.4 表的元方法

元方法 描述
__tostring 自定义 tostring 函数的行为
__call 使表可以像函数一样调用
__len 自定义 # 操作符(表的长度)
__pairs 自定义 pairs 函数的行为
示例:__tostring

local mt = {
    __tostring = function(table)
        return "This is a custom table"
    end
}

local tbl = {}
setmetatable(tbl, mt)

print(tbl) -- 输出:This is a custom table
示例:__call

local mt = {
    __call = function(table, name)
        print("Hello, " .. name)
    end
}

local tbl = {}
setmetatable(tbl, mt)

tbl("Lua") -- 输出:Hello, Lua
示例:__len

local mt = {
    __len = function(table)
        return 42
    end
}

local tbl = {}
setmetatable(tbl, mt)

print(#tbl) -- 输出:42
示例:__pairs
local mt = {
    __pairs = function(table)
        return function(_, key)
            return next({x = 1, y = 2, z = 3}, key)
        end
    end
}

local tbl = {}
setmetatable(tbl, mt)

for k, v in pairs(tbl) do
    print(k, v) -- 输出:x 1, y 2, z 3
end

3. 实现面向对象编程

元表在 Lua 中常用于实现面向对象编程。


3.1 简单类的实现

示例:创建类

-- 定义类
local Person = {}
Person.__index = Person

function Person:new(name, age)
    local obj = {name = name, age = age}
    setmetatable(obj, self)
    return obj
end

function Person:greet()
    print("Hello, my name is " .. self.name)
end

-- 创建实例
local p = Person:new("Alice", 25)
p:greet() -- 输出:Hello, my name is Alice

3.2 类的继承

示例:继承实现

-- 定义父类
local Animal = {}
Animal.__index = Animal

function Animal:new(name)
    local obj = {name = name}
    setmetatable(obj, self)
    return obj
end

function Animal:speak()
    print(self.name .. " makes a sound")
end

-- 定义子类
local Dog = setmetatable({}, {__index = Animal})

function Dog:speak()
    print(self.name .. " barks")
end

-- 创建实例
local dog = Dog:new("Buddy")
dog:speak() -- 输出:Buddy barks

4. 小结

元方法 描述 示例
__index 自定义表中不存在的键的访问行为 动态字段、继承
__newindex 自定义表中不存在的键的赋值行为 只读表、代理字段
运算符元方法 重载运算符 __add, __eq,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小宝哥Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值