天天看点

Lua面向对象编程 (__index、__newindex、__call、__add)

Lua面向对象编程

1、__index学习

【前置知识】

setmatatable:设置元表

setmetatable(table1, metatable)

metatable里一般实现__index、__newindex、__add等元方法实现对table的某种操作

当__index为表时

通过键值访问table时,如果没有这个键,那么Lua就会寻找table的metatable中__index表里的key键

性质很像继承关系的父亲

【代码】

t1 = {area = 0}

t2 = setmetatable(t1, {__index = {width = 1}})
-- 此时t1 等价t2 有相同的地址空间
print(t1.area)
print(t1.width)
t1.area = 2
print(t2.area)
print(t2.width)
t2.width = 2
print(t1.area)
print(t1.width)      

【运行结果】

Lua面向对象编程 (__index、__newindex、__call、__add)

__index实现继承关系

【代码】

--继承关系
Shape = {area = 0}
function Shape:new(o, side)
  o = o or {}
  setmetatable(o, self)
  self.__index = self  --保持继承链,不加此行, t1就没有__index元方法,下面的t2 = it:new({}, 4)会报错
  side = side or 0
  o.area = side * side
  return o
end

t1 = Shape:new({key1 = 1}, 3)
print("t1.area:", t1.area)
print("t1.key1:", t1.key1)
t2 = t1:new({key2 = 2}, 4)
print("t2.key1:", t2.key1)
print("t2.key2:", t2.key2)
t3 = t2:new({key3 = 3}, 5)

print("t3.key1:", t3.key1)
print("t3.key2:", t3.key2)
print("t3.key3:", t3.key3)      

【运行结果】

Lua面向对象编程 (__index、__newindex、__call、__add)

当__index为函数时

通过table.key查找时,会将table和key作为参数传给函数

【代码】

t = setmetatable({key1 = "hsj"}, {__index = function(table, key)
  if key == 'key2' then
    return 'yes'
  else
    return 'no'
  end
end})

print(t.key2)      

【运行结果】

Lua面向对象编程 (__index、__newindex、__call、__add)

2、__newindex学习

正常的table当对表中一个不存的key进行赋值,会产生一个新的key值保存。当用了__newindex后,则table不会产生新的key值,而对metatable产生新的key保存

【代码】

t = {}
print("t.key:", t.key)
t1 = setmetatable({},{__newindex = t})
t1.key = 1
print("t1.key:", t1.key)
print("t.key:",t.key)      

【运行结果】

Lua面向对象编程 (__index、__newindex、__call、__add)
__index和__newindex混合使用

【代码】

t2 = {}
t = setmetatable({key1 = "hsj"}, {__index = function(table, key)
  if key == 'key2' then
    return 'yes'
  else
    return 'no'
  end
end,
__newindex = t2
})

t.key2 = 2
print("t2.key2:", t2.key2)
print("t.key2:", t.key2)      

【运行结果】

Lua面向对象编程 (__index、__newindex、__call、__add)

【私密性】

相当于private

__index和__newindex创建一个只读的table

--只读table

function readOnly(t)
  local answer = {}
  local mt =  {
    __index = t,
    __newindex = function(t, k, v)
      error("attempt to update a read-only table", 2)
    end
  }

  setmetatable(answer, mt)
  return answer
end

t1 = {1, 2, 3, 4, 5}

t2 = readOnly(t1)

print(t2[1])

t2[1] = 5   --无法修改      

__call

__call 元方法在 Lua 调用一个值时调用。

self或表名均可

【代码】

mytable = setmetatable({10}, {
__call = function(self, value)
  print("value:", value)
end
})
print(mytable(10))

mytable = setmetatable({10}, {
__call = function(mytable, value)
  print("value:", value)
end
})
print(mytable(10))      

【运行结果】

Lua面向对象编程 (__index、__newindex、__call、__add)
重载__add元方法
--只读table

t = setmetatable({1, 2, 3},{
  __add = function(self, newtable)
    len1 = #self
    len2 = #newtable
    sum = 0
    for i=1, len1 do
      sum = sum + self[i]
    end
    for i=1, len2 do
      sum = sum + newtable[i]
    end
    return sum

  end
})


t1 = {2,3,4}

answer = t + t1
print("answer:", answer)      

其他类似的元方法

__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

__len(a)                        对应表达式 #a

__eq(a, b)                      对应表达式 a == b

__lt(a, b)                      对应表达式 a < b