Lua怎么链式使用Metatables?

文章导读
Previous Quiz Next 链式元表是一种非常有效的技术,用于在 Lua 中实现类似继承的功能,并根据需要将操作委托给不同的 table 或对象。链式是通过使用 __index() 元方法实现的。
📋 目录
  1. A __index 作为 table 的工作原理
  2. B 示例 - 使用 __index 作为 table 的链式元表
  3. C __index 作为函数的工作原理
  4. D 链式 Metatables 的优势
A A

Lua - 元表链式



Previous
Quiz
Next

链式元表是一种非常有效的技术,用于在 Lua 中实现类似继承的功能,并根据需要将操作委托给不同的 table 或对象。链式是通过使用 __index() 元方法实现的。

链式元表可以被视为 table 的回退机制。当我们尝试访问 table 中不存在的键时,Lua 会查找其元表中的 __index() 方法实现。如果 __index() 是另一个 table,那么 Lua 会继续在下一个 table 中搜索,并持续形成一个元表链来查找 __index() 方法实现。

__index 作为 table 的工作原理

  • 考虑一个对象或 table 作为 table1,其中缺少某个键。

  • 为该 table 设置一个元表 metatable1,即 setmetatable(table1, metatable1)

  • metatable1 有一个 __index 字段指向另一个 table,即 table2。

  • 现在 Lua 首先由于 __index 字段而在 table2 中查找缺失的键。如果找到键,则返回 table2 中的值。

  • 如果键未找到且 table2 又有一个 metatable2,其 __index 设置为 table3,则 Lua 会继续在 table3 中搜索,依此类推。

示例 - 使用 __index 作为 table 的链式元表

让我们创建一个 Animal → Dog → Pug 的继承链。

main.lua

-- 基类:Animal
Animal = { sound = "Generic Roar" }

Animal.__index = Animal 

-- 方法 makeSound
function Animal:makeSound()
  print(self.sound)
end

-- 子类:Dog,继承自 Animal 类
Dog = setmetatable({ breed = "Bulldog" }, { __index = Animal })

Dog.__index = Dog

-- Dog 类特有的方法
function Dog:bark()
  print("Woof!")
end

-- 重写父类方法
function Dog:makeSound()
  print("Woof woof!")
end

-- 子类:Pug,继承自 Dog 类
Pug = setmetatable({ name = "PUG" }, { __index = Dog })
-- 用于 : 语法调用方法时。
Pug.__index = Pug 

-- Pug 类特有的方法
function Pug:snore()
  print("Zzz..")
end

-- 创建 Animal 实例
local animal = {}
setmetatable(animal, { __index = Animal })

-- 创建 Dog 实例
local dog = {}
setmetatable(dog, { __index = Dog })

-- 创建 Pug 实例
local pug = {}
setmetatable(pug, { __index = Pug })

-- 链式过程
animal:makeSound()       -- Generic Roar

dog:makeSound()          -- Woof woof! (重写)
dog:bark()               -- Woof! (继承)
print(dog.sound)         -- Generic Roar (继承)
print(dog.breed)         -- Bulldog (自身属性)

pug:makeSound()          -- Woof woof! (从 Dog 继承)
pug:bark()               -- Woof! (从 Dog 继承)
pug:snore()              -- Zzz.. (自身方法)
print(pug.sound)         -- Generic Roar (从 Animal 继承)
print(pug.breed)         -- Bulldog (从 Dog 继承)
print(pug.name)          -- PUG (自身属性)

输出

运行上述程序时,我们将得到以下输出−

Generic Roar
Woof woof!
Woof!
Generic Roar
Bulldog
Woof woof!
Woof!
Zzz..
Generic Roar
Bulldog
PUG

__index 作为函数的工作原理

我们也可以将 __index 元方法用作函数。这个函数将返回 table,并且现在它将以先前解释的相同方式工作,

main.lua

-- Table 1
Table1 = { value1 = "Hello" }

-- Table 2
Table2 = { value2 = "World" }
setmetatable(Table1, { __index = Table2 })

-- 创建实例
local instance = {}
setmetatable(instance, {
  __index = function(table, key)
    print("正在检查缺失的键 '" .. key .. "'")  -- 翻译注释
    return Table1[key]
  end
})
-- 打印:正在检查缺失的键 'value1' \n Hello
print(instance.value1)
-- 打印:正在检查缺失的键 'value2' \n World
print(instance.value2) 
-- 打印:正在检查缺失的键 value3 \n nil
print(instance.value3)

输出

运行上述程序时,我们将得到以下输出−

Checking for missing key 'value1'
Hello
Checking for missing key 'value2'
World
Checking for missing key 'value3'
nil

链式 Metatables 的优势

  • 继承 − 我们可以创建子类的层次结构,从超类继承属性。

  • 可重用性 − 可以在基类中定义通用代码,然后由子类重用。

  • 委托 − 可以将某些操作委托给专用的对象,例如日志记录。

  • 组织 − 可以通过定义 table,并将它们与专用的 table 链式连接来组织代码,等等。