Lua中self 、自索引及其面向对象应用代码示例

简介: Lua中self 、自索引及其面向对象应用代码示例

一、Lua表的self标识

在lua中,表拥有一个标识:self。self类似于c++中的this指针和python中的self。在lua中,提供了冒号操作符来隐藏这个参数,例如:

t1 = {
    id = 1, 
    name = "panda",
    addr = "beijing"
}
-- 使用冒号语法实现
function t1:getId()
    return self.id
end
function t1:setId(val)
    self.id = val
end
t1:setId(10)
print(t1:getId())
-- 使用点语法实现
function t1.getName(obj)  -- 需要将表作为参数传入
    return obj.name
end
function t1.setName(obj, val)
    obj.name = val
end
t1.setName(t1, "mark")
print(t1.getName(t1))

运行结果:

冒号只是一种语法机制,提供的是便利性,并没有引入任何新的东西。使用冒号完成的事情,都可以使用点语法来完成。

两者有什么区别呢?

  1. lua 为冒号提供了独有的指令:self
  2. 从用法上来说,使用点语法实现需要传入表对象,而冒号就显得较为简洁了。

二、自索引

t2 = {id = 2, name = "lwang", age = 18}
meta_t2 = {
    -- __index = meta_t2,  -- 为什么写在这里不行,因为此时 meta_t2 还不存在,所以是nil
    addr = "shenzhen"
}
meta_t2.__index = meta_t2;  -- 自索引,自己索引自己
setmetatable(t2, meta_t2)
print(t2.addr)

运行结果:

三、自索引实现继承

father = {a = 1, b = 2}
function father:fatherSayHello()
    print("father say hello: ", father.a, father.b)
end
father.fatherSayHello()
father.__index = father
son = {a = 10, b = 20}
function son:sonSayHello()
    print("son say hello")
end
son.sonSayHello()
setmetatable(son, father)  -- 设置元表
son.sonSayHello()
son.fatherSayHello()  --子类son有元表,并且有__index = father,指向父类的表,调用父类的方法

运行结果:

四、Lua 模拟类的实例化(带参构造)

-- 4.  面向对象 类的实例化(带参构造)
stuInfo = {
    id = 1, 
    name = "panda",
    age = 18
}
-- stuInfo stu1 stu2 都是同一张表
stu1 = stuInfo
stu2 = stu1
print(stuInfo, stu1, stu2)
print(stuInfo.id, stu1.id, stu2.id)
stuInfo.id = 100
print(stuInfo.id, stu1.id, stu2.id) -- 都会休改为:100
print("")
stuInfo.__index = stuInfo  -- 设置自索引
function stuInfo:newInstance(tab)
    obj = tab or {}
    setmetatable(obj, self) -- 为obj设置元表为self 也就是stuInfo  谁调用newInstance,self就是谁
    -- self.__index = self
    return obj
end
-- stu3 接收newInstance出来的实例
stu3 = stuInfo:newInstance({id = 3, age = 20})
print("stu3: ", stu3.id, stu3.name, stu3.age)  -- name会使用stu3 元表也就是stuInfo中的name的值
print("")
stu4 = stu3:newInstance()
print("stu4: ", stu4.id, stu4.name, stu4.age)  -- 输出 nil nil nil 
print("")
stu3.__index = stu3   -- 设置自索引
stu5 = stu3:newInstance({id = 1111})
print("stu5: ", stu5.id, stu5.name, stu5.age)  -- 输出 nil nil nil 
print("")

运行结果:

结果分析:

为什么stu3可以正常输出,stu4为什么输出都是nil?

因为:stu3 = stuInfo:newInstance({id = 3, age = 20}) ,stu3 在实例化的过程中,传了参数:id 和 age 所以,id 和 age 直接输出3 和 20 没有问题,在stu3输出name的时候,因为stu3 没有name索引,会找自己的元表,也就是stuInfo,触发 stuInfo的__index因为stuInfo.__index 指向的是自己 (设置了自索引 stuInfo.__index = stuInfo),所以会到stuInfo表中找name 找到 name = “panda” 输出:panda

而,stu4是通过stu3实例化出来的(stu4 = stu3.newInstance())且没有传递参数,在输出stu4的id name 和 age 时,本身都没有,就会找stu4的元表,stu4的元表是stu3。 关键就在于 stu3 没有__index元方法,所以stu4输出的都是nil

如何让stu4 正常输出呢?两种解決方法:

-- 1. 为stu3 设置自索引
    stu3.__index = stu3  
    -- 2. 直接在newInstance 方法中 通过 self.__index = self 为调用者设置自索引
    function stuInfo:newInstance(tab)
        obj = tab or {}
        setmetatable(obj, self) -- 为obj设置元表为self 也就是stuInfo  谁调用newInstance,self就是谁
        self.__index = self  -- 为调用者设置自索引
        return obj
    end

五、多重继承

grandfather = {id = 1, name = "grandfather"}
grandfather.__index = grandfather
father = {name = "father"}
father.__index = father
-- 设置father的元表为grandfather
setmetatable(father, grandfather)
son = {name = "son"}
-- 设置son的元表为father
setmetatable(son, father)
print(grandfather.id, father.id, son.id) -- 输出: 1 1 1
print(grandfather.name, father.name, son.name) -- 输出: grandfather     father  son

运行结果:

结果分析:

先分析id:

  • grandfather.id 因为grandfather有id 所以直接输出1
  • father.id 因为father没有id索引,会找fahter的元表,也就是grandfather,触发grandfather的__index 此时__index指向grandfather本身,grandfather本身就是一张表,就会找自己有没有id索引,找到id = 1返回
  • son.id 因为son没有id索引,会找son的元表,也就是father,触发father的__index 此时__index指向father本身, father本身就是一张表,就会找自己有没有id索引,找到id = 1返回, 没有继续找会找fahter的元表,也就是grandfather,触发grandfather的__index 此时__index指向grandfather本身,grandfather本身就是一张表,就会找自己有没有id索引,找到id = 1返回

分析name:

name 索引因为都有,所以正常输出

六、子类重写父类方法

stuInfo = {
    id = 1, 
    name = "panda",
    age = 18,
    say = function()
        print("stuInfo say.")
    end
}
function stuInfo:newInstance(tab)
    obj = tab or {}
    setmetatable(obj, self) -- 为obj设置元表为self 也就是stuInfo  谁调用newInstance,self就是谁
    self.__index = self  -- 设置自索引
    return obj
end
function stuInfo:getId()
    return self.id
end
function stuInfo:setId(newId)
    self.id = newId
end
stu1 = stuInfo:newInstance()
stu2 = stu1:newInstance()
print(stuInfo, stu1, stu2)
print(stuInfo.id, stu1.id, stu2.id)  -- 如果子类没有赋值,默认会使用父类的值;如果父类的成员或方法发生变化会影响子类,前提是子类没有重写
stuInfo.age = 25
print(stuInfo.age, stu1.age, stu2.age)
stu1.id = 100   -- stu1以下都会改变  这就是重写
print(stuInfo.id, stu1.id, stu2.id)
print("")
stuInfo.say()
stu1.say()
stu2.say()
print("")
function stu1:say()    -- stu1 重写say方法
    print("stu1 say.")
end
stuInfo.say()
stu1.say()    -- stu1 调用自己的say方法
stu2.say()    -- stu2 调用stu1的say方法

运行结果

七、成员私有化

function personInfo()
    -- 将成员属性隐藏起来
    local members = {
        id = 1,
        name = "lwang",
        age = 25
    }
    local function getId()
        return members.id
    end
    local function getName()
        return members.name
    end
    local function setId(id)
        members.id = id
    end
    local function setName(name)
        members.name = name
    end
    -- 对外暴露的方法
    return {
        getId = getId,
        setId = setId,
        getName = getName,
        setName = setName
    }
end
p1 = personInfo()
print(p1.getId(), p1.getName())
p1.setId(22)
p1.setName("mark")
print(p1.getId(), p1.getName())

运行结果

相关文章
|
1月前
|
存储 监控 数据管理
Lua代码解析:实现上网行为监管软件的自定义规则引擎
在当今数字化时代,网络安全和数据隐私保护备受关注。为了确保网络安全和合规性,许多组织和机构需要监管和管理其员工或用户的上网行为。为了实现这一目标,开发一款高效的上网行为监管软件至关重要。本文将介绍如何使用Lua语言开发一种自定义规则引擎,以实现上网行为监管软件的自定义规则引擎。
103 0
|
10月前
|
运维 5G Go
Go或者C中调用Lua业务脚本,实现终端应用的热更新方案
Go或者C中调用Lua业务脚本,实现终端应用的热更新方案
|
11月前
Lua面向对象
Lua面向对象
60 0
|
XML Java 数据格式
【Lua基础 第3章】变量、赋值语句、索引、lua中的循环、循环控制语句
lua 中的变量、赋值语句、索引、lua中的循环、循环控制语句
117 0
【Lua基础 第3章】变量、赋值语句、索引、lua中的循环、循环控制语句
|
消息中间件 数据采集 Java
Lua集成kafka第三方插件代码介绍|学习笔记
快速学习Lua集成kafka第三方插件代码介绍
103 0
Lua集成kafka第三方插件代码介绍|学习笔记
|
缓存 NoSQL Java
基于代码实操SpringBoot、Redis、LUA秒杀系统!
基于代码实操SpringBoot、Redis、LUA秒杀系统!
175 0
|
监控 安全 测试技术
【Lua】代码执行覆盖率
以黑盒测试来讲,简单概括为以下几个方面来进行保障: 1. 在产品的需求分析阶段,参与需求的评审,发现需求设计中的不合理性以及存在风险的设计等,从源头发现问题,这样可以花费最小的代价获得最高的收益。 2. 在开发设计编码阶段,如果有设计评审环节,需要参与设计讨论,这样就可以尽早的了解技术的实现逻辑,获取更多影响测试质量的因素,完善测试用例。可能在游戏行业很少会进行编码设计环节的评审,但作为测试人员最好可以在技术忙碌之余主动去了解设计实现,知道技术设计的逻辑流程,从而编写具有针对性的测试用例。
294 0
【Lua】代码执行覆盖率
【Lua篇】静态代码扫描分析(四)规则检查
通过前面三篇文章已经初步实现了将Lua源代码文件读取解析成语法树,现在就可以通过得到的语法树进行指定规则的代码扫描检查。下图简单列举了一下单个Lua文件内部的语法关系情况(注意并非真正的类图,也没有列举完全部的节点类型)。
424 0
【Lua篇】静态代码扫描分析(四)规则检查