元表概念
Lua中,面向对象是用元表这种机制来实现的
元表(matatable)
Lua在创建新的table时不会创建元表,比如以下代码就可以演示:
local t = {1, 2}
print(getmetatable(t)) -- nil
设置元表和获取元表(getmetatable和setmetatable)
使用getmetatable来获取一个table或userdata类型变量的元表,当创建新的table变量时,使用getmetatable去获得元表,将返回nil;同理,我们也可以使用setmetatable去设置一个table或userdata类型变量的元表
local t = {}
print(getmetatable(t)) -->nil 创建新的table,还没有设置元表,故为nil
local t1 = {}
setmetatable(t, t1) --设置元表
assert(getmetatable(t) == t1)
任何table都可以作为任何值得元表,而一组相关的table有可以共享一个通用的元表,此元表描述了它们共同的行为。一个table甚至可以作为它自己的元表,用于描述其特有的行为。总之,任何搭配形式都是合法的
Set = {}
local mt = {} -- 集合的元表
-- 根据参数列表中的值创建一个新的集合
function Set.new(l)
local set = {}
setmetatable(set, mt) --每调用一次这个函数,就创建一个set,并设置元表mt
for _, v in pairs(l) do set[v] = true end
return set
end
-- 并集操作
function Set.union(a, b)
local retSet = Set.new{} -- 此处相当于Set.new({})
for v in pairs(a) do retSet[v] = true end
for v in pairs(b) do retSet[v] = true end
return retSet
end
所有由Set.new创建的集合都具有一个相同的元表,例如
local set1 = Set.new({10, 20, 30})
local set2 = Set.new({1, 2})
print(getmetatable(set1))
print(getmetatable(set2))
assert(getmetatable(set1) == getmetatable(set2)) --他们的元表都是一样的
最后,我们需要把元方法加入元表中,代码如下:
mt.__add = Set.union
local set1 = Set.new({10, 20, 30})
local set2 = Set.new({1, 2})
local set3 = set1 + set2
Set.print(set3)
保护元表
保护元表,用字段__metatable。当我们想要保护集合的元表,是用户既不能看也不能修改集合的元表,那么就需要使用__metatable字段了;当设置了该字段时,getmetatable就会返回这个字段的值,而setmetatable则会引发一个错误;如以下演示代码:
function Set.new(l)
local set = {}
setmetatable(set, mt)
for _, v in pairs(l) do set[v] = true end
mt.__metatable = "You cannot get the metatable" -- 设置完我的元表以后,不让其他人再设置
return set
end
local tb = Set.new({1, 2})
print(tb)
print(getmetatable(tb)) --得到You cannot get the metatable
setmetatable(tb, {}) -- 这儿已经不能设置元表的了
__index元方法:
默认情况下,当我们访问一个table中不存在的字段时,得到的结果是nil。但是这种状况很容易被改变;Lua是按照以下的步骤决定是返回nil还是其它值得:
当访问一个table的字段时,如果table有这个字段,则直接返回对应的值;
当table没有这个字段,则会促使解释器去查找一个叫__index的元方法,接下来就就会调用对应的元方法,返回元方法返回的值;
如果没有这个元方法,那么就返回nil结果。
Student = {} -- 创建一个命名空间
-- 创建默认值表
Student.default = { name= "zhangsan", age = 18, id = 1101 }
Student.mt = {} -- 创建元表
-- 声明构造函数
function Student.new(o)
setmetatable(o, Student.mt) --设置元表
return o
end
-- 定义__index元方法
Student.mt.__index = function (table, key)
return student.default[key]
end
local s = Student.new(name = "lisi")
print(s.name) -- >lisi 访问自身已经拥有的值
print(s.age) -- >18 访问default表中的值
print(s.id) -- >1101 访问default表中的值