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

news2024/11/27 20:26:54

一、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())

运行结果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/143035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

快过年了,看我使用python制作一个灯笼的小程序

哈喽呀,最近一直在写一些关于新年的的python小东西,今天也不例外,我把那些都放到了一个专辑里面了,感兴趣的朋友们可以去看看一下哦 目录 前言 画外轮廓 画灯笼的提线 画灯笼盖 画一下各种柱子 小小的外壳来一下 其他的小装饰…

Python打包(问题记录,待解决)

引言 文章用于测试在Python3.8的版本,打包Obspy地震包,最后集成到PyQt5上。 部署或冻结应用程序是 Python 项目的重要组成部分, 这意味着捆绑所有必需的资源,以便应用程序找到它需要的一切 能够在客户端计算机上运行。 但是&…

基于Python Unet的医学影像分割系统源码,含皮肤病的数据及皮肤病分割的模型,用户输入图像,模型可以自动分割去皮肤病的区域

手把手教你用Unet做医学图像分割 我们用Unet来做医学图像分割。我们将会以皮肤病的数据作为示范,训练一个皮肤病分割的模型出来,用户输入图像,模型可以自动分割去皮肤病的区域和正常的区域。废话不多说,先上效果,左侧…

JAVA语言基础语法——JVM虚拟机默认处理异常的方式,try...catch捕获异常

1.JVM默认的处理方式 a.把异常的名称,异常原因及异常出现的位置等信息输出在了控制台 运行结果如下: b.程序停止执行,异常下面的代码不会再执行了 2.try...catch捕获异常(自己处理,捕获异常&am…

数学建模-相关性分析(Matlab)

注意:代码文件仅供参考,一定不要直接用于自己的数模论文中 国赛对于论文的查重要求非常严格,代码雷同也算作抄袭 如何修改代码避免查重的方法:https://www.bilibili.com/video/av59423231 //清风数学建模 一、基础知识 1.皮尔逊…

Qt之Json操作demo

一、JSON简介: JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,使用JavaScript语法来描述数据对象,但是JSON仍然独立于语言和平台。JSON解析器和JSON库支持许多不同的编程语言,被广泛用于Internet上的数据交换格式。…

多线程高级(线程状态、线程池、volatile、原子性、并发工具)

1.线程池 1.1 线程状态介绍 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢?Java中的线程 状态被定义在了java.lang.Thread.State…

Java程序:jstack

前言 如果有一天,你的Java程序长时间停顿,也许是它病了,需要用jstack拍个片子分析分析,才能诊断具体什么病症,是死锁综合征,还是死循环等其他病症,本文我们一起来学习jstack命令~ jstack 的功能…

阳康后的第一篇博客,先来几道恶心二进制编程题

目录 一、统计二进制中1的个数 二、打印整数二进制的奇数位和偶数位 三、两个整数二进制位不同个数 一、统计二进制中1的个数 这是一道牛客网OJ题,感兴趣的话可以先做一遍再看解析哦 -> 牛客网的OJ链接 注意:上面的牛客网是接口型,不需…

Vagrant管理已存在的虚拟机

起因 某天打开VirtualBox后,发现之前创建的虚拟机都没了,后将虚拟机从本地磁盘又重新导入,但是发现使用 vagrant up 会创建新的虚拟机,而我要用vagrant管理已存在的虚拟机,就是 vagrant up的时候,我需要启动…

MySQL数据库:常用数据类型

一、整形和浮点型 数据类型大小说明对应C类型bit(n)n指定比特位数,默认1位比特位数,n范围1-64;存储数值范围2-2^n-1char[]tinyint1字节signed charsmallint2字节short intint4字节intbigint8字节long long intfloat(m,d)4字节单精度&#xf…

【前端】CSS基础

一、CSS基础 1.1CSS的介绍 CSS:层叠样式表(Cascading style sheets) CSS作用是什么?? 1.2CSS语法规则 写在哪里? CSS是style标签中,style标签一般写在head标签里面,title标签下…

真的强,又一个开源项目,杀疯了

最近,AI大模型连续火爆出圈,人工智能生成模型(AIGC)的热度尚未褪去,聊天机器人ChatGPT便引发全网热议,两周吸引百万用户。还有卷趴一半程序员的AlphaCode,生成全新蛋白质的ESM2等,不…

TypeScript,终于在实际项目中用到了泛型。

终于在实际项目中用到了泛型 里程碑式的纪录,终于不是anyScript了,代码写完只有一个any 应用 项目中组件化了这么一个东西 功能描述: 传进去一个数组,有个名,有个key,渲染成上图的div样式点击之后&#…

Triple 协议支持 Java 异常回传的设计与实现

作者:陈景明 背景 在一些业务场景,往往需要自定义异常来满足特定的业务,主流用法是在catch里抛出异常,例如: public void deal() {try{//doSomething ...} catch(IGreeterException e) {...throw e;} }或者通过…

Python面向对象(九)

python学习之旅(九) 👍查看更多可以关注查看首页或点击下方专栏目录 一.什么是面向对象 万物皆对象 现实世界的事物都有属性和行为,可在程序中抽离为类来描述现实世界的事物属性和行为。 使用类充当程序内现实事物的“设计图纸”,基于图纸(类)生产实体…

10. 正则表达式匹配

题目链接:https://leetcode.cn/problems/regular-expression-matching/从暴力递归到动态规划,对于状态转移方程不容易推导出来的可以先从递归进行尝试各种策略,最后再从暴力递归转为动态规划,这种尝试方式容易求解dp初始值以及dp更…

数据结构-考研难点代码突破 (图关键路径完全解析(流程+代码) - C++代码)

考研在关键路径上的考察以流程为主 文章目录1. AOE网2. 关键路径问题解决流程C代码1. AOE网 首先区分AOV网&#xff1a; AOV网∶若用DAG 图&#xff08;有向无环图&#xff09;表示一个工程&#xff0c;其顶点表示活动&#xff0c;用有向边<Vi&#xff0c;Vj>表示活动 V…

【ESP32+freeRTOS学习笔记-(五)队列Queue】

目录1、什么是队列Queue2、队列的多任务特性2.1 多任务的访问&#xff1a;2.2 队列读取阻塞&#xff1a;2.3 写队列阻塞&#xff1a;2.4 阻塞于多个队列&#xff1a;3、队列的使用3.1 创建队列--The xQueueCreate() API3.2 写入队列3.3 从队列中接收数据3.4 删除队列4、队列集4…

ReactDOM.render在react源码中执行之后发生了什么?

ReactDOM.render 通常是如下图使用&#xff0c;在提供的 container 里渲染一个 React 元素&#xff0c;并返回对该组件的引用&#xff08;或者针对无状态组件返回 null&#xff09;。本文主要是将ReactDOM.render的执行流程在后续文章中会对创建更新的细节进行分析&#xff0c…