1. 程式人生 > >【Lua特性】Metatable(元表)

【Lua特性】Metatable(元表)

__index 元方法

主要用於table的查詢,其可為table或function.

__newindex 元方法

主要用於table的更新(賦值),其可為table或function.

local parent = {
    atk  = 1
}

local child = {
    hp = 2
}

parent.__index = parent

setmetatable(child, parent)

print(child.atk)  -- ot:1
print(child.hp)   -- ot:2


parent.__newindex = function ( t, k ,v )
	if k == "atk" then
		parent[k] = v * 2
	end
end

child.atk = 3
child.hp = 4

print(child.atk)  -- ot:6
print(child.hp)   -- ot:4

parent.__index = nil

print(child.atk)  -- ot:nil
print(child.hp)   -- ot:4

Lua查詢一個表元素時的規則,其實就是如下3個步驟:

    1. 在表中查詢,如果找到,返回該元素,找不到則繼續     2. 判斷該表是否有元表,如果沒有元表,返回nil,有元表則繼續。     3. 判斷元表有沒有__index方法,如果__index方法為nil,則返回nil;如果__index方法是一個表,則重複1、2、3;如果__index方法是一個函式,則返回該函式的返回值。

 操作符

local v1 = { x = 1, y = 2 }
local v2 = { x = 3, y = 4 }

local meta = {}

meta.__add = function ( lhs, rhs )
	local ret = {}
	ret.x = lhs.x + rhs.x
	ret.y = lhs.y + rhs.y

	return ret
end

setmetatable(v1, meta)
setmetatable(v2, meta)

local v3 = v1 + v2

print(v3.x, v3.y) -- 4, 6
模式 描述
__add(a, b) a + b
__sub(a, b) a - b
__mul(a, b) a * b
__div(a, b) a / b
__mod(a, b) a % b
__pow(a, b) a ^ b
__unm(a) -a
__concat(a, b) a .. b
__eq(a, b) a == b
__lt(a, b) a < b
__le(a, b) a <= b

__call 元方法

把table當做函式呼叫


local meta = {}

meta.__call = function ( t1, t2 )
    local sum = 0
    
	for _, v in pairs(t1) do
		sum = sum + v
	end
    
    for _, v in pairs(t2) do
        sum = sum + v
    end

    return sum
end

local newTable = { 1, 2 }
setmetatable(newTable, meta)
-- 上面兩行,可寫成 local newTable = setmetatable({ 1, 2 }, meta)

print(newTable({3, 4, 5})) -- 15

 __tostring


local meta = {}

meta.__tostring = function ( t )
    local ret = ""
    
	for _, v in pairs(t) do
		ret = ret .. " " .. v
	end

    return "表元素: " .. ret
end

local newTable = setmetatable({1, 2, 3, 4}, meta)

print(newTable) -- 表元素:  1 2 3 4

用法例項:

local Stack = { data = {} }

function Stack:new()
	self.__index = self

	return setmetatable({ data = {} }, self)
end

function Stack:add(name, age)
	table.insert(self.data, {["name"] = name, ["age"] = age})
end

function Stack:get( name )
	for k, v in pairs(self.data) do
		if v.name == name then
			return v, k
		end
	end

	return nil, 0
end

function Stack:getByIdx( idx )
	return self.data[idx]
end

function Stack:size()
	return #self.data
end

local studentList = Stack:new()
studentList:add("小明", 10)
studentList:add("小紅", 12)
studentList:add("小蘭", 11)

local s1 = studentList:get("小蘭")

print(s1.name .. "  " .. s1.age) -- 小蘭  11
print("學生數量: " .. studentList:size()) -- 學生數量: 3
--預設值
function setDefault ( t , default )
	local mt = {}
	mt.__index = function ( )
		return default
	end

	setmetatable(t , mt)
end
 
local v = { x = 1 ,y = 2 }
print(v.x , v.y , v.z) -- 1 2 nil

-- 設定預設值
setDefault(v, 0)

print(v.x , v.y , v.z) -- 1 2 0
 
 
--只讀屬性
function setReadOnly ( t )
	local mt = {}
	mt.__index = t
	mt.__newindex = function (  )
		print("it's a readOnly table")
	end

	return setmetatable({} , mt) 
end

local data = setReadOnly({ 1, 2, 3, 4 })

print(1) -- 1
data[1] = 11 -- it's a readOnly table