您的位置:首页 > 编程语言 > Lua

Lua的面向对象

2017-01-07 15:56 260 查看

__index和__newindex

__index元方法用来对表访问,__newindex元方法用来对表更新 。

当通过键来访问table的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index键。如果__index指向一个表格,Lua会在表格中查找相应的键,如果__index指向一个函数,Lua会将以table和key作为参数调用该函数,并返回该函数的返回值。

访问表元素时的规则:

1.在表中查找,如果找到,返回该元素,找不到则继续

2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续

3.判断元表有没有__index键,如果__index为nil,则返回nil;如果__index指向一个表,则重复1、2、3;如果__index指向一个函数,则返回该函数的返回值

更新表元素的规则:

1.如果table有该元素,直接为该元素更新值;

2.如果table无该元素,且table的元表为nil或元表不为nil但元表的__newindex为nil,则直接为table添加元素;

3.如果table无该元素,且存在元表,元表的__newindex不为nil,则通过__newindex来操作,具体是:如果__newindex指向一个表,重复上述步骤;如果__newindex指向一个函数,以table,key,value作为参数调用该函数。

基于对象

一个简单实例:

Rectangle = {area = 0, length = 0, breadth = 0}

function Rectangle:new (o,length,breadth)
o = o or {}
setmetatable(o, self)
self.__index = self
self.length = length or 0
self.breadth = breadth or 0
self.area = length*breadth
return o
end

function Rectangle:printArea ()
print("矩形面积为",self.area)
end

myshape = Rectangle:new(nil,2,10)
myshape:printArea()


这里的
:
是一个语法糖,定义函数时,相当于把
:
换成
.
并将self作为第一个形参;调用函数时,相当于把
:
换成
.
并将调用函数的table作为第一个实参。

还原后的代码:

Rectangle = {area = 0, length = 0, breadth = 0}

function Rectangle.new (self,o,length,breadth)
o = o or {}
setmetatable(o, self)
self.__index = self
self.length = length or 0
self.breadth = breadth or 0
self.area = length*breadth
return o
end

function Rectangle.printArea (self)
print("矩形面积为 ",self.area)
end

myshape = Rectangle.new(Rectangle,nil,2,10)
myshape.printArea(myshape)


在myshape.printArea(myshape)中:

因为myshape里边没有printArea,去找元表Rectangle的__index,__index指向Rectangle,Rectangle里有printArea,所以就去调用Rectangle的printArea。在Rectangle的printArea里打印myshape.area,myshape里没有area,又去Rectangle的__index里找,找到并打印了出来。

但是在这里,数据不是保存在new出来的实例中的,而是保存在Rectangle中,这就像c++里类的静态成员变量和静态成员函数。比如这样用:

myshape1 = Rectangle:new(nil,2,5)
myshape2 = Rectangle:new(nil,4,10)
myshape1:printArea()
myshape2:printArea()


打印出来的都是40。

我们换一种更好的方式

Rectangle = {}
Rectangle.__index = Rectangle

function Rectangle.new (length,breadth)
o = {}
setmetatable(o, Rectangle)
o.length = length or 0
o.breadth = breadth or 0
o.area = o.length*o.breadth
return o
end

function Rectangle:printArea ()
print("矩形面积为",self.area)
end

myshape1 = Rectangle.new(2,10)
myshape2 = Rectangle.new(4,10)
myshape1:printArea()
myshape2:printArea()


面向对象

通过__index实现继承的例子如下:

Rectangle = {}
Rectangle.__index = Rectangle

function Rectangle.new(o, length, breadth)
o = o or {}
o.length = length or 0
o.breadth = breadth or 0
o.area = o.length * o.breadth
setmetatable(o, Rectangle)
return o
end

function Rectangle:getArea()
return self.length * self.breadth
end

Cube = {}
setmetatable(Cube, Rectangle)
Cube.__index = Cube

function Cube.new(o, length, breadth, height)
o = o or {}
Rectangle.new(o, length, breadth)
o.height = height or 0
setmetatable(o, Cube)
return o
end

function Cube:getVolume()
return self.height*self:getArea()
end

r1 = Rectangle.new(nil,2,3)
r2 = Rectangle.new(nil,3,4)
print("r1的长是", r1.length, "宽是", r1.breadth)
print("r1的面积是", r1:getArea())
print("r2的面积是", r2:getArea())

c1 = Cube.new(nil,4,5,6)
c2 = Cube.new(nil,3,4,5)
print("c1的面积是", c1:getArea())
print("c1的体积是", c1:getVolume())
print("c2的面积是", c2:getArea())
print("c2的体积是", c2:getVolume())

--[[输出是:
r1的长是        2       宽是    3
r1的面积是      6
r2的面积是      12
c1的面积是      20
c1的体积是      120
c2的面积是      12
c2的体积是      60
]]--


关键在于
setmetatable(Cube, Rectangle)和setmetatable(o, Cube)
,在c1中查找getVolume时,会在c1的元表Cube的__index指向的Cube中查找,正好找得到;在c1中查找getArea时,却找不到,就去Cube的元表Rectangle的__index指向的Rectangle中查找,也找到了。

虽然Lua也可以实现封装、抽象、派生,但也有点太麻烦了,设计简单,用起来就麻烦,OO也不是Lua擅长的,这种事情交给c++来做就好了。

比较像JavaScript的原型链,但简单多了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  lua