lua实现面向对象(一)

c++和java语言机制中自己带有面向对象的内容,而lua设计的思想是元编程,没有面向对象的实现。java

可是利用lua的元表(matetable)机制,能够实现面向对象。要讲清楚怎样实现lua面向对象,须要讲清楚如下内容。c++

1.lua元表 2.类和对象 3.继承 编程

 

1.lua元表数据结构

lua里的全部数据结构都是表。metatable能够改变table的行为。例如加法行为,table自己没有加法行为。lua

能够经过修改元表中的__add域,来提供表的加法行为。__add叫作元方法(metamethod)。spa

一个表默认是不带元表的。getmetatable能够获取元表。设计

t = {}
print(getmetatable(t)) --nil

setmetatable能够设置元表。code

t = {}
setmetatable(t, t) --设置本身是本身的元表
print(t == getmetatable(t)) --true

任何一个表能够其余表的元表。一个表也能够是本身的元表。对象

来看一个展现__add元方法的例子。实现一个集合的并集运算。blog

Set = {}

Set.mt = {}

Set.new = function (t)
    local res = {}
    setmetatable(res, Set.mt)
    for k, v in pairs(t) do    --用原始集合的value做为新集合的key
        res[v] = true            --新集合的value值 能够是任意值
    end
    return res
end

Set.union = function (a, b)
    local res = Set.new{}    --res将Set.mt做为元表
    for k, v in pairs(a) do res[k] = true end    --res的全部key值做为新的并集,value值能够是任意值
    for k, v in pairs(b) do res[k] = true end
    return res
end

Set.mt.__add = Set.union

Set.print = function (t)
    local s = "{ "
    for k, v in pairs(t) do
        s = s .. tostring(k) .. " "    --全部key值做为新的并集
    end
    s = s .. "}"
    print(s)
end

s1 = Set.new{1, 2, 3, "hello"}
s2 = Set.new{2, 3, 4, "world"}

s3 = s1 + s2

Set.print(s3)   --{ 1 2 3 4 hello world}

在new集合时将一个公共的mt表做为了集合的元表,那么经过new建立的集合都会有相同的元表,也就有了相同的行为。

再经过定义__add域,则改变了表的“+”行为,使得“+”变成了集合的并集运算。

能够重定义的元方法以下:

__add --加法
__sub --剑法
__mul --乘法
__div --除法
__unm --
__pow --
__concat --链接
__eq    --等于
__lt    --小于
__le    --小于等于
__tostring --字符串输出
__index    --访问表的域
__newindex --更新表的域
__metatable --使元表不能被修改

其中最重要的__index,__newindex是用来实现面向对象的关键。下面一个对表的监控例子,能够看出__index,__newindex的做用。

original = {} --原始表

mt = {}

mt.__index = function (t, k) --此表的访问操做,都会访问original表
    print("access table element " .. tostring(k) .. " : " .. tostring(original[k]))
    return original[k]
end

mt.__newindex = function (t, k, v) --此表的赋值操做,都会操做original表
    print("update table element " .. tostring(k) .. " : " .. tostring(original[k]) .. " to " .. tostring(v))
    original[k] = v
end

t = {} --监控表 用来监控original

setmetatable(t, mt)
t[1] = "hello"            --update table element 1 : nil to hello
str = t[1] .. "world"    --access table element 1 : hello
print(str)                --helloworld

 当咱们访问一个表的不存在的域时,会触发访问__index方法。当表缺乏一个赋值域时,会触发访问__newindex方法。

这两个重要的元方法是实现面向对象的关键。

 

2.类和对象

咱们建立一个对象做为其余对象的原型,当调用不属于该对象的方法时,会去原型中查找。

setmetatable(a, {__index = b})

则b是原型,a是原型的对象。概念上称b是类,a是b的实例对象。调用a中不存在的对象时,会去b中查找。

Cal = {}

function Cal:New(o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
end

function Cal:Add(a, b)
    print("Cal Add")
    return a + b
end

a = Cal:New()
print(a:Add(5, 6)) --11

调用Cal:New实际会返回一个以Cal本身为元表的新对象。而a中没有Add方法,则会在Cal中查找Add方法。

3.继承

有了类与对象,咱们须要在此基础上实现继承。

Cal = {}

function Cal:New(o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
end

function Cal:Add(a, b)
    print("Cal Add")
    return a + b
end

SubCal = Cal:New()

function SubCal:Add(a, b)
    print("SubCal Add")
    return a + b
end

a = SubCal:New()
print(a:Add(5, 6)) --11

一个SubCal子类,从基类Cal中继承了Add方法。a对象会首先在SubCal中查询方法,若是有则调用,所以子类能够复写父类的方法。若是没有则去到父类中查找方法。

相关文章
相关标签/搜索