到目前为止,我们见过的所有元方法针对的都是核心 Lua
语言。 Lua
语言虚拟机会检测一个操作中涉及的值是否有存在的对应元方法的元表。不过,由于元表是一个普通的表,所以任何人都可以使用他们。因此,程序库在元表中定义和使用他们自己的字段也是一种常见的实践。
函数 tostring
就是一个典型的例子。函数 tostring
能将表表示为一种简单的文本格式:
print({}) --> table: 0x8062ac0
函数 print
总是调用 tostring
来进行格式化输出。不过,当对值进行格式化时,函数 tostring
会首先检查值是否有一个元方法 __tostring
。如果有,函数 tostring
就会调用这个元方法来完成工作,将对象作为参数传给该函数,然后把元方法的返回值作为函数 tostring
的返回值。
在之前集合的示例中,我们已经定义了一个将集合表示为字符串的函数。因此,只需要在元表中设置 __tostring
字段:
mt.__tostring = Set.tostring
之后,当一个集合作为参数调用函数 print
时, print
就会调用函数 tostring
, tostring
又会调用 Set.tostring
:
s1 = Set.new{10, 4, 5} print(s1) --> {4, 5, 10}
函数 setmetatable
和 getmetatable
也用到了元方法,用于保护元表。假设想要保护我们的集合,就要使用户既不能看到也不能修改集合的元表。如果在元表中设置 __metatable
字段,那么 getmetatable
会返回这个字段的值,而 setmetatable
则会引发一个错误:
mt.__metatable = "not your business" s1 = Set.new{} print(getmetatable(s1)) --> not your business setmetatable(s1, {}) stdin:1: cannot change protected metatable
从 Lua 5.2
开始,函数 pairs
也有了对应的元方法,因此我们可以修改表被遍历的方式和为非表的对象增加遍历行为。当一个对象拥有 __pairs
元方法时, pairs
会调用这个元方法来完成遍历。