Lua 面向对象

这篇具有很好参考价值的文章主要介绍了Lua 面向对象。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

Lua 中一张表 table 就是一个对象(注意不是类,是对象)。他具有以下的特性:

  1. 表和对象都可以拥有状态
  2. 表和对象都拥有一个与其值无关的标识(self)
  3. 两个具有相同值的对象是两个不同的对象,而一个对象可以具有多个不同的值
  4. 表和对象一样,具有与创建者和被创建者位置无关的生命周期

二、类

1、 Lua 的类

Lua 并没有类这一概念,而是通过 “基于原型” 的方式进行组织,而原型其实也是一个实例对象。

换而言之,我们通过元表的方式,让 Lua 的实例对象在找不到对应的属性或方法时,从其元表的 __index 属性指定的表(即上面所说的原型,其实他也是实例对象)或方法中获取,而如果这里指定的表也没有对应的属性,则继续往该表(也就是原型)的元表继续查找,直到查找到或全部查找完还没有找到对应的属性或方法为止。

2、self 字段

在类中,this(在 Lua 中是 self) 很重要,因为他可以让不同实例调用同一方法不会有互相干扰

举个例子

下面的例子中,withdraw 方法内部固定写了 Account ,所以只要将 Account 删除,则会有异常

local Account = { balance = 0 }
function Account.withdraw(v)
    Account.balance = Account.balance - v
end

Account.withdraw(100)
print(Account.balance)              --> -100

local a, Account = Account, nil
a.withdraw(100)                     --> 这里会报错,因为 withdraw 内部使用了 Account ,而这个值已经被移除

所以这里如果有一个可以指向自身的指针就可以避免这一问题,则将 function Account.withdraw(v) 方法多加一个 self 参数,来决定函数内部的操作是针对哪个实例,变成为 function Account.withdraw(self, v) ,改变后的代码:

local Account = { balance = 0 }
function Account.withdraw(self, v)
    self.balance = self.balance - v
end

local a = Account
Account = nil
a.withdraw(a, 100)
print(a.balance)

但是这样的使用就会麻烦一些,每次使用都需要将自己传入,Lua 有一个语法糖,如果第一个参数是指向自身,则可以使用冒号(:)进行调用。

同时也可以使用冒号(:)进行定义方法,这样会自动在参数的最前面添加一个 self 的参数,方法内部就可以进行使用了,具体代码如下:

冒号定义的方法,也可以使用点方式调用,只是需要传入多一个参数指向自己

local Account = { balance = 0 }
-- 使用 : 就相当于 function Account.withdraw(self, v)
function Account:withdraw(v)
    self.balance = self.balance - v
end

local a = Account
Account = nil
a:withdraw(100)
print(a.balance)

-- 和上面一样
a.withdraw(a, 100)
print(a.balance)

3、类的 “实例化”

首先,创建一个 Account ,这个就类似 java、kotlin 中的类,但其实在 Lua 中,他是一个 table 实例( Lua 中没有类概念)。

local Account = { balance = 0 }
function Account:new(o)
    o = o or {}
    -- 将自己设置为 __index 的元方法
    self.__index = self
    -- 将自己设置为 o 的元表,这样就会调用 self 的 __index 方法或表,这里就是 self 自身表
    setmetatable(o, self)
    return o
end
-- 使用 : 就相当于 function Account.withdraw(self, v)
function Account:withdraw(v)
    self.balance = self.balance - v
end

Account 给自己添加一个 __index 元方法,并指向自己,然后将自己设置给新创建的 table 作为元表,最后将该 table 返回,他就是我们需要的对象了。

然后,进行创建和使用,这样的两个实例就不会互相影响。

local a = Account:new()
a:withdraw(100)
print("a.balance", a.balance)       --> a.balance	-100
-- 这里调用 withdraw 后,a 和 b 自身就有 balance 字段了,也就不需要进行元表的查询
local b = Account:new()
print("b rawget", rawget(b, "balance")) --> b rawget	nil
b:withdraw(1000)
print("b.balance", b.balance)           --> b.balance	-1000
print("b rawget", rawget(b, "balance")) --> b rawget	-1000

print("a.balance", a.balance)           --> a.balance	-100

值得一提的是,在经过 withdraw 方法之后, a 和 b 两个实例就都有了各自的 balance 属性。

一图胜千言

Lua 面向对象,Lua,lua,android,开发语言,c++,c语言

4、继承

从上面的图可以知道,我们只需要在这个元表的搜索链插入我们需要的节点,则能够达到继承的效果。

还是基于上面的 Account 代码,继承一个 SpecialAccount 类(其实也是一个实例对象,Lua 中没有类概念,只是我们叫法的区分)

local SpecialAccount = Account:new()
-- 重写了 withdraw 方法
function SpecialAccount:withdraw(v)
    print(self, "SpecialAccount withdraw")
    if v - self.balance >= self:getLimit() then
        error "insufficient funds"
    end
    self.balance = self.balance - v
end
-- 增加方法
function SpecialAccount:getLimit()
    print(self, "SpecialAccount getLimit")
    return self.limit or 0
end

创建对象,则用 SpecialAccount 进行创建,进行调用 withdraw 方法时,这时调用的则就是 SpecialAccount 中定义的方法

local person = SpecialAccount:new({ limit = 1000 })
person:withdraw(10)
print(person.balance)
--> table: 0x600002f4c240	Account new
--> table: 0x600002f4c4c0	SpecialAccount withdraw
--> table: 0x600002f4c4c0	SpecialAccount getLimit
--> -10

这里关键在于 local person = SpecialAccount:new({ limit = 1000 }) 的时候,new 方法内部的 self 指向的是 SpecialAccount ,所以元表链就建立起来了。

一图胜千言

Lua 面向对象,Lua,lua,android,开发语言,c++,c语言

5、为实例添加额外方法

和 java、kotlin 不太一样的是,Lua 的对象可以自行添加一些属性或方法,当然也可以重写父类的属性或方法,因为本质上 Lua 的对象就是一个表。

基于上面的代码,我们对 person 进行重写 getLimit 方法,这样在调用 withdraw 方法时,内部调用的就是 person 对象的 getLimit 方法

-- 给 person 自定义一个方法
function person:getLimit()
    print(self, "person getLimit")
    return self.balance * 0.10
end
-- 这个时候的限制就变为了自身的 getLimit
person.balance = 100000
person:withdraw(10)     
print(person.balance)       
--> table: 0x600002f4c4c0	SpecialAccount withdraw
--> table: 0x600002f4c4c0	person getLimit
--> 99990

6、多重继承

在 java、kotlin 中,没有多继承这一概念。而在 Lua 中,可以非常容易的实现,因为搜索链的本质是基于元表中的 __index 方法。之前都是设置的表,如果需要多重继承,只需要设置为方法,在方法中实现相应的搜索方式即可。


local function search(k, plist)
    for i = 1, #plist do
        local v = plist[i][k]
        if v then
            return v
        end
    end
end

function createClass(...)
    local c = {}
    local parents = { ... }

    c.__index = function(t, k)
        local v = search(k, parents)
        -- 这里可以保存下来,只是后续修改方法的定义就会比较困难,因为不会再走元表链
        -- t[k] = v
        return v
    end

    function c:new(o)
        o = o or {}
        setmetatable(o, c)
        return o
    end

    return c
end

只需要通过 createClass 方法,传入需要继承的父类,然后在 __index 的函数中搜索这些父类是否有满足的属性或方法即可。其余的操作和单继承是一样的。

继续使用之前的 Account 类,我们多编写一个 Named 类,进行多继承使用

local Named = {}
function Named:getname()
    return self.name
end
function Named:setname(n)
    self.name = n
end

local NamedAccount = createClass(Named, Account)
local account = NamedAccount:new { name = "jiang", balance = 10000 }
print(account:getname())                --> jiang
account:setname("jiang peng yong")      
print(account:getname())                 --> jiang peng yong
account:withdraw(100)
print(account.balance)                  --> 9900

一图胜千言

Lua 面向对象,Lua,lua,android,开发语言,c++,c语言

三、私有性

Lua 没有私有性机制,一般把需要私有性的名称最后加上一个下画线,用于区分全局和私有。

除了约定的方式外,还可以使用闭包来达到私有性

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

local account = newAccount(1000)
account.deposit(10000)
account.withdraw(59)
print(account.getBalance())     --> 10941

在上面的代码中,self 外部不可访问,从而达到私有性。

四、写在最后

Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)

如果觉得本篇博文对你有所启发或是解决了困惑,点个赞或关注我呀

公众号搜索 “江澎涌”,更多优质文章会第一时间分享与你。文章来源地址https://www.toymoban.com/news/detail-708346.html

到了这里,关于Lua 面向对象的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • LUA 对象转excel

    因为是excel, 所以第一层要是数组,否则没有什么意义,即lua对象要是一个数组比较合理。这里使用开源的json.lua, 但是开源的,对于数字作下标的,或者是一个数组里,不同类型的key混合的情况无法转换,所以我进行了一定的改进,先进行了扫描判断是不是混合的key,是的话

    2024年01月18日
    浏览(37)
  • lua学习笔记18(面相对象之多态)

    输出  

    2024年04月13日
    浏览(35)
  • XLua中lua读写cs对象的原理

    XLua在C#维护了两个数据结构,ObjectPool和ReverseMap。 首次传递一个C#对象obj到Lua时,对象被加入到ObjectPool中,并为它创建一个唯一标识objId,建立obj和objId的双向映射。 如果该对象的类型是第一次传到Lua,还会为类型创建一个元表typeMetatable。 把typeMetatable注册到Lua的全局表中,这

    2024年02月08日
    浏览(46)
  • 【Lua】VSCode 搭建 Lua 开发环境

    最近在找工作,基本所有的岗位都会问到 Lua(甚至拼 UI 的都要求会 Lua),咱能怎么办呢,咱也只能学啊…… 工欲善其事,必先利其器。第一步,先来把环境配置好吧! 当前适用版本: LuaBinaries 版本:5.4.2 VSCode 版本:1.77.3 文章最近更新日期:2023.04.24   Lua 官网:The Progra

    2023年04月24日
    浏览(52)
  • 【Lua】(一)VSCode 搭建 Lua 开发环境

    最近在找工作,基本所有的岗位都会问到 Lua(甚至拼 UI 的都要求会 Lua),咱能怎么办呢,咱也只能学啊…… 工欲善其事,必先利其器。第一步,先来把环境配置好吧! 当前适用版本: LuaBinaries 版本:5.4.2 VSCode 版本:1.77.3 文章最近更新日期:2023.04.24 Lua 官网:The Programmi

    2024年02月11日
    浏览(45)
  • [Lua] 探讨类与对象机制的实现、并实现抽象工厂模式

    Lua 没有严格的 oo(Object-Oriented)定义,可以利用元表特性来实现 先定义所有类的基类,即 Object 类。代码顺序从上到下,自成一体。完整代码 定义一个空表 Object , __index 指向其自身(继承将直接使用该表作为对象的元表) new 定义构造对象时的初始化行为,相当于 构造器

    2024年02月09日
    浏览(33)
  • Lua语言实现游戏动作

    Lua是一种轻量级的脚本语言,它具有高效性、可扩展性和易学性等优点。在游戏开发领域,Lua语言得到了广泛应用。Lua语言可以用来实现游戏动作,包括角色行走、攻击、技能释放等。本文将详细介绍如何使用Lua语言实现游戏动作。 一、Lua语言介绍 Lua语言是一种轻量级的脚

    2023年04月14日
    浏览(42)
  • C语言调用lua

    欢迎关注公众号:fensnote C语言是一种非常流行的编程语言,而Lua是一种基于C语言开发的脚本语言。相信大家都知道,Lua可以使用C语言来扩展其功能,进而实现更复杂的功能。而在Lua的各种实现中,luajit也是其中一种非常流行的实现。在本篇博客中,我将为大家介绍如何使用

    2024年02月07日
    浏览(36)
  • Lua脚本语言

    Lua(发音为\\\"loo-ah\\\",葡萄牙语中的\\\"lua\\\"意为月亮)是一种轻量级的、高效的、可嵌入的脚本编程语言。官网Lua最初由巴西计算机科学家Roberto Ierusalimschy、Waldemar Celes和Luiz Henrique de Figueiredo于1993年开发,它的设计目标是提供一种简单的、易扩展的脚本语言,特别适用于嵌入到其他

    2024年02月07日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包