前言
Lua语言中的一张表就是一个对象。首先,表与对象一样,可以拥有状态。其次,表与对象一样,拥有一个与其值无关的的标识(self);特别地,两个具有相同值的对象(表)是两个不同的对象,而一个对象可以具有多个不同的值;最后,表与对象一样,具有与创建者和被创建位置无关的生命周期。
Account = {balance = 20}
function Account.withdraw(v)
Account.balance = Account.balance - v
end
Account.withdraw(10)
print(Account.balance) --> 10
Account使用全局名称,只能针对特定方法
修改后就不能访问了
a, Account = Account, nil
a.withdraw(10)
print(Account.balance) --> ERROR!
a和Account公用同一段地址,这时Account赋值为nil,当调用a.withdraw(10)时,其实是调用Account.withdraw,而Account已经为nil了,所以出错
解决办法:可以看到a还保留着Account,可以将a当成参数传进去,这样就可以a.withdraw()等于Account.withdraw
Account = {balance = 20}
function Account.withdraw(self, v)
self.balance = self.balance - v
end
a, Account = Account, nil
a.withdraw(a, 10)
print(a.balance) --> 10
self参数大多数编程语言都是隐藏的,Lua可以使用:隐藏该参数
Account = {balance = 20}
function Account:withdraw(v)
self.balance = self.balance - v
end
a, Account = Account, nil
a:withdraw(10)
print(a.balance) --> 10
--不使用:调用也需要传入参数
function Account:deposit(v)
self.balance = self.balance + v
end
Account.deposit(Account, 30)
Account.deposit(30) --> 报错
print(Account.balance) --> 40
类
采用原型的方式
当对象(类的实例)遇到一个未知操作时会首先在原型中查找。
例:如果有两个对象A和B,要让B成为A的一个原型
setmetatable(A, {__index = B})
在此之后,A就会在B中查找所有它没有的操作。
例如:使用__index元方法从Account继承这些操作(假设Account是具有多种方法的函数)
Account = {balance = 20}
function Account:deposit(v)
self.balance = self.balance + v
end
local mt = {__index = Account}
function Account.new(o)
o = o or {} -- 如果Account没有则创建一个新表
setmetatable(o, mt) -- 将mt转成o类型,即o继承mt的所有发那个发
return o
end
a = Account.new{balance = 20}
a:deposit(100)
print(a.balance) --> 120
当a时,a会将mt作为其元表。当调用
a:deposit(100.00)时,实际上调用的是a.deposit(a,100.00)。不过,Lua语言无法在表a中找到字段"deposit",所以它会在元表的__index中搜索。此时的情况大致如下:getmetatable(a).__index.deposit(a, 100.00)a的元表是
mt,而mt.__index是Account。因此,上述表达式等价于:Account.deposit(a,100.00)即,Lua语言调用了原来的
deposit函数,传入了a作为self参数。因此,a从Account继承了函数deposit。同样,它还从Account继承了所有的字段。
改动
function Account.new(o)
o = o or {}
self.__index = self -- 如果Account没有则创建一个新表
setmetatable(o, self) -- 将mt转成o类型,即o继承mt的所有发那个发
return o
end
b = Account.new()
print(b.balance) --> 20
b中Account.new没传参,则创建新的表,继承自Account
继承
基类
Account = {balance = 0}
function Account:new(o)
o = o or {}
self.__index = self
setmetatable(o, self)
return o
end
function Account:deposit(v)
self.balance = self.balance + v
end
function Account:withdraw(v)
if v > self.balance then
error"insufficient funds"
end
self.balance = self.balance - v
end
派生类
SpecialAccount = Account:new() --实现继承基类
s = SpecialAccount:new{limit = 100}
s:deposit(50)
print(s.balance) --> 50
SpecialAccount就像继承其他方法一样从Account继承了new。不过,现在执行new时,它的self参数指向的是SpecialAccount。因此, s的元表会是SpecialAccount,其中字段__index的值也是SpecialAccount。因此,s继承自SpecialAccount,而SpecialAccount又继承自Account。之后,当执行s:deposit(100.00)时,Lua语言在s中找不到deposit字段,就会查找SpecialAccount,仍找不到deposit字段,就查找Account并最终会在Account中找到deposit的最初实现。
可以对基类方法重定义
function SpecialAccount:withdraw(v)
if v - self.balance >= self:getLimit() then
error "insufficient funds"
end
self.balance = self.balance - v
end
function SpecialAccount:getlimit()
return self.limit or 0
end
这样调用
withdraw时,会现在SpecialAccount中查找
私有性
function newAccount(initialBalance)
local self = {balance = initialBalance}
local withdraw = function(v)
self.balance = self.balance - v
end
local deposit = function(v)
self.balance = self.balance + v
end
local getBalance = function()
return self.balance
end
return {
withdraw = withdraw,
deposit = deposit,
getBalance = getBalance
}
end
acc = newAccount(100, 100)
acc.withdraw(40)
print(acc.getBalance()) --> 60
只能通过
newAccount来访问
function newAccount(initialBalance)
local self = {
balance = initialBalance,
LIM = 10000.00
}
local extra = function()
if self.balance > self.LIM then
return self.balance*0.10
else
return 0
end
end
local getBalance = function()
return self.balance + extra()
end
end