通常, Lua
语言中的每种类型的值都有一套可预见的操作集合。例如,我们可以将数字相加,可以连接字符串,可以在表中插入键值对等。但是,我们无法将两个表相加,无法对函数作比较,也无法调用一个字符串,除非使用元表。
元表可以修改一个值在面对一个未知操作时的行为。例如,假设 a
和 b
都是表,那么可以通过元表定义 Lua
语言如何计算表达式 a + b
。当 Lua
语言试图将两个表相加时,它会先检查两者之一是否有元表且该元表中是否有 _add
字段。如果 Lua
语言找到了该字段,就调用该字段对应的值,即所谓元方法(是一个函数)。
可以认为,元表是面向对象领域中的受限制类,像类一样,元表定义的是实例的行为。不过,由于元表只能给出预先定义的操作集合的行为,所以元表比类更受限;同时,元表不支持继承。不过尽管如此,我们依然可以基于元表构建一个相对完整的类系统;
Lua
语言中的每一个值都可以有元表。每一个表和用户数据类型都具有各自独立的元表,而其他类型的值则共享对应类型所属的同一个元表。 Lua
语言在创建新表时不带元表:
t = {} print(getmetatable(t)) --> nil
可以使用函数 setmetatable
来设置或修改任意表的元表:
t1 = {} setmetatable(t, t1) print(getmetatable(t) == t1) --> true
在 Lua
语言中,我们只能为表设置元表,如果要为其他类型的值设置元表,则必须通过 C
代码或调用库完成(该限制存在的主要原因是为了防止过度使用对某种类型的所有值生效的元表。 Lua
语言老版本中的经验表明,这样的全局设置经常导致不可重用的代码)。字符串标准库为所有的字符串都设置了同一个元表,而其他类型在默认情况下都没有元表:
print(getmetatable("hi")) --> table 0x80772e0 print(getmetatable("hello")) --> table 0x80772e0 print(getmetatable(10)) --> nil print(getmetatable(print)) --> nil
一个表可以称为任意值的元表,一组相关的表也可以共享一个描述了他们共同行为的通用元表,一个表还可以成为它自己的元表,用于描述其自身特有的行为。总之,任何配置都是合法的。