前言
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