lua学习笔记day13-----类,继承,多继承

在lua的手册中,描述,lua是不存在类这种概念的。或者说是因为lua中对象太强大,可以做到类的所有事情。
不过lua可以做到模仿类的这种方式,也是非常有好处的。
lua中会把一个或者多个对象作为原型,然后去继承它们,得到新的对象。
lua中的类技术,最关键的点有两个,一个是关键字self,它可以保证其他对象可以使用这个对象的函数。
另一个是__index,这个是实现继承的基础。当然有一种继承是不通过__index,最后会介绍。
下面通过一个例子来解释继承:

Account={
        balance = 0
}
function Account:New(o)
        o = o or {}
        setmetatable(o,self)
        self.__index = self
        return o
end
function Account:depesit(v)
        self.balance = self.balance + v
end
function Account:withdraw(v)
        if v > self.balance then error "no !" end
        self.balance = self.balance - v
end
SpecialAccount = Account:New{}
function SpecialAccount:getlimit()
        return self.limit or 0
end
function SpecialAccount:withdraw(v)
        if v - self.balance > self:getlimit() then
                error "can't!"
        end
        self.balance = self.balance - v
end
s = SpecialAccount:New{limit = 1000}
do
s:withdraw(10000)
end

我们将Accout这个对象作为原型,在它的内部建立了New,depesit,withdraw三个函数。
然后通过这个原型,声明一个子类SpecialAccount(也是个对象)。
最后通过SpecialAccount声明了一个s的实例。

当s:withdraw(10000)运行的时候,lua发现在s中没有withdraw这个函数,于是跑到s的metatable的__index中寻找,s的metatable的__index是SpecialAccount,在SpecialAccount中,lua发现了withdraw函数,就继续执行。
在SpecialAccount的withdraw中,lua发现没有balance这个值,于是就向s的metatable的__index(SpecialAccount)查找,没找到,就向SpecialAccount的metatable的__index(Account)查找,然后发现balance = 0;
拿到balance的值之后,继续向下执行,lua又发现调用了getlimit函数,s中也没有这个函数,但是在SpecialAccount中找到了。拿到相应的值之后开始计算。

上述过程中,最重要的是理解self这个关键字是怎么工作的。
举个例子 :
Account:withdraw(v)
self.balance = self.balance + v
end
等价于
Account.withdraw(self.v)
self.balance = self.balance + v;
当在程序运行过程中,s调用了withdraw -> s:withdraw(v)
就等于s.withdraw(s,v);
也正因为这样的机制,使得原型所有的函数,子类和实例都可以调用。
这样的继承方式,使得扩展变的非常的困难,因为一旦需要新的方法,就得重写实例,或者原型,下面介绍一种多继承,来解决这个问题。
多继承
实现多继承的方法:
在继承中,我们提到是将对象的matatable的__index指向一个原型对象,这样就可以继承原型的所有方法。而多继承,是将对象的matatable的__index指向一个函数,这个函数维护一张表,表里放了所有需要继承的父类。
当用这个对象创建实例的时候,实例会把这个对象变成自己的metatable的__index。所以实例会间接的继承所有的父类。
代码如下:

Account={
        balance = 0
}
function Account:New(o)
        o = o or {}
        setmetatable(o,self)
        self.__index = self
        return o
end
function Account:depesit(v)
        self.balance = self.balance + v
end
function Account:withdraw(v)
        if v > self.balance then error "no !" end
        self.balance = self.balance - v
end
SpecialAccount = Account:New{}
function SpecialAccount:getlimit()
        return self.limit or 0
end
function SpecialAccount:withdraw(v)
        if v - self.balance > self:getlimit() then
                error "can't!"
        end
        self.balance = self.balance - v
end
Named = {}
function Named:getname()
        return self.name
end
function Named:setname(n)
        self.name = n
end
local function Search(k,plist)
        for i = 1,#plist do
                local v = plist[i][k]
                if v then return v end
        end
end
function CreatClass(...)
        local c = {}
        local arg = {...}
        setmetatable(c,{__index = function(t,k)
                return Search(k,arg)
        end})
        c.__index = c
        function c:New(o)
                o = o or {}
                setmetatable(o,c)
                return o
        end
        return c
end
do
NamedAccount = CreatClass(Named,Account)
account = NamedAccount:New{name = "linxin"}
print(account:getname())
end

(在上述程序中,函数的可变参数里,在lua的5.2版本之前,都是默认放在arg的表里,5.2之后改版了,需要声明一个表来存放接收到的可变参数内容)
creatcalss接收到参数之后,通过c的metatable的__index产生一个闭包,保存了接收到的所有可变参数。
当程序运行 print(account:getname()) 的时候,lua在account中找不到getname函数,随后调用account的metatable的__index metamethod 在保存的表中的对象里寻找。然后再Named中找到了getname方法。
可以看到用这样的方法,就可以使实例继承多个父类中的方法。

猜你喜欢

转载自blog.csdn.net/displaymessage/article/details/80085105
今日推荐