Lua元表

       元表是Lua中最强大的特性之一,本节将深入讲解元表的工作原理、常用元方法、以及如何在Android开发中利用元表实现高级功能。

1. 元表基础

1.1 设置元表

-- 创建普通表
local t = {x = 10, y = 20}

-- 创建元表
local mt = {
    __index = function(t, k)
        return "字段 "..k.." 不存在"
    end
}

-- 设置元表
setmetatable(t, mt)

print(t.z)  -- 输出"字段 z 不存在"

1.2 获取元表

-- 获取已设置的元表
local meta = getmetatable(t)
if meta then
    print("该表有元表")
end

-- 保护元表(禁止修改)
setmetatable(t, {
    __metatable = "锁定的元表"
})
print(getmetatable(t))  -- 输出"锁定的元表"
setmetatable(t, {})    -- 会报错

2. 常用元方法

2.1 算术运算元方法

local Vector = {}
Vector.__index = Vector

function Vector.new(x, y)
    return setmetatable({x = x, y = y}, Vector)
end

-- 定义向量加法
function Vector.__add(a, b)
    return Vector.new(a.x + b.x, a.y + b.y)
end

-- 使用示例
local v1 = Vector.new(1, 2)
local v2 = Vector.new(3, 4)
local v3 = v1 + v2  -- 调用__add
print(v3.x, v3.y)   -- 输出4 6

2.2 关系运算元方法

-- 定义向量相等比较
function Vector.__eq(a, b)
    return a.x == b.x and a.y == b.y
end

-- 定义向量大小比较
function Vector.__lt(a, b)
    return (a.x^2 + a.y^2) < (b.x^2 + b.y^2)
end

-- 使用示例
print(v1 == v2)  -- false
print(v1 < v3)   -- true

3. 访问控制元方法

3.1 __index和__newindex

-- 创建只读表
function readOnly(t)
    return setmetatable({}, {
        __index = t,
        __newindex = function()
            error("该表是只读的", 2)
        end
    })
end

-- 使用示例
local config = readOnly{
    version = "1.0",
    theme = "dark"
}
print(config.version)  -- 输出"1.0"
config.version = "2.0" -- 报错

3.2 默认值表

-- 带默认值的表
function withDefault(default)
    return setmetatable({}, {
        __index = function() return default end
    })
end

-- 使用示例
local scores = withDefault(60)
print(scores["Alice"])  -- 输出60

4. 调用与操作元方法

4.1 __call元方法

-- 使表可被调用
local logger = setmetatable({}, {
    __call = function(self, msg)
        print(os.date() .. " - " .. msg)
    end
})

-- 像函数一样调用表
logger("系统启动")  -- 输出"2023-11-20 10:00:00 - 系统启动"

4.2 __tostring元方法

-- 自定义表输出格式
function Vector.__tostring(self)
    return string.format("Vector(%d, %d)", self.x, self.y)
end

-- 使用示例
print(v1)  -- 输出"Vector(1, 2)"

5. 高级元表技巧

5.1 原型继承

-- 实现原型继承
function inherit(proto)
    local inst = {}
    setmetatable(inst, {
        __index = proto,
        __call = proto.__call
    })
    return inst
end

-- 使用示例
local parent = {name = "Parent"}
local child = inherit(parent)
print(child.name)  -- 输出"Parent"

5.2 方法链

-- 链式调用
local chain = setmetatable({}, {
    __index = function(self, key)
        return function(...)
            -- 执行操作
            return self  -- 返回自身实现链式
        end
    end
})

-- 使用示例
chain:move(10,20):rotate(45):scale(2)

6. Android开发实践

6.1 Java对象包装

-- 包装Android View
function wrapView(view)
    return setmetatable({view = view}, {
        __index = function(t, k)
            -- 优先查找Lua方法
            local method = rawget(t, k)
            if method then return method end
            
            -- 其次调用Java方法
            return function(...)
                return t.view[k](t.view, ...)
            end
        end
    })
end

-- 使用示例
local button = wrapView(findViewById(R.id.button))
button:setText("Click me")  -- 调用Java方法

6.2 性能优化

元表使用建议:

7. 调试与陷阱

7.1 元表调试

-- 检查元方法
function dumpMeta(t)
    local mt = getmetatable(t)
    if mt then
        for k, v in pairs(mt) do
            print(k, type(v) == "function" and "function" or v)
        end
    end
end

7.2 常见问题

注意事项:

  • __index__newindex会相互影响
  • 元表循环引用会导致内存泄漏
  • 某些元方法必须成对实现(如__eq__lt
  • rawget/rawset可以绕过元方法