Lua本身是没有class之类的关键字的,但是我们可以巧妙利用function也是值和table的特性来实现面向对象的特性。

通过复制表的实现

Lua中的类也是一个table对象,下面我们看看一个简单的类的定义:

 People = {}--定义表 People

 --添加方法的第一种方式
People.talk = function()
print("people talk.")
end --添加方法的第二种方式
function People.say()
print("people say.")
end People.talk()--people talk.
People.say()--people say.

我们发现添加一个方法有两种方式,这两种方式的效果都是一致的,至于使用哪种就看个人喜好了。

通过拷贝创建实例

不知道大家发现没有,上面的示例中并没有创建实例这个步骤,而在面向对象中,类是需要创建实例来使用的,下面我们就用复制的方法来创建实例:

 --克隆一个表
function clone(obj)
local instance = {} for key, var in pairs(obj) do
instance[key] = var
end return instance;
end People = {}--定义表 People --定义方法
People.talk = function()
print("people talk.")
end --创建 People 的实例
local p = clone(People)
p.talk()--people talk.

构造函数

我们发现虽然可以创建实例了,但是却没有构造函数,下面我们把构造函数也添加进去:

 --克隆一个表
function clone(obj)
local instance = {} for key, var in pairs(obj) do
instance[key] = var
end return instance;
end People = {}--定义表 People --定义构造函数
People.new = function(name)
local ins = clone(People)
ins.name = name;
return ins
end --定义方法
People.talk = function(self)
print(self.name.." talk.")
end --创建 People 的实例
local p = People.new("Li Lei")
p.talk(p)--people talk.
p:talk()--使用 : 号可以省略 self 的填写, 效果和上一行一致

这里引入了一个新的变量self和“:”符号,下面我们来说说:

self

self作为第一个参数表示调用则本身。

.号和:号

调用时为了传递调用者本身,所以第一个参数需要将自身传入,这是.号的写法;

如果使用:号,则默认第一个参数传入调用者本身,无需我们再写第一个参数了,我们可以从第二个参数开始写,但是函数声明时仍然要写第一个参数self的。

定义函数时也可以使用:号进行定义,这样定义方法时也可以省略self参数,但是写法只能写成function tab:foo() end这样的形式,如下:

 obj = {name = "Li Lei"}
--.号的写法和调用
obj.foo1 = function()
print "foo1"
end
function obj.foo2(self)
print(self.name.."foo2")
end
obj.foo1()--foo1
obj.foo2(obj)--Li Leifoo2
--:号的写法和定义
--[[注释掉的这种写法是不允许的
obj:foo3 = function()
print(self.name.."foo3")
end
]]
function obj:foo4()
print(self.name.."foo4")
end
obj:foo4()--Li Leifoo4

继承

下面我们来看看如何实现类的继承:

 --克隆一个表
function clone(obj)
local instance = {} for key, var in pairs(obj) do
instance[key] = var
end return instance;
end People = {}--定义表 People --定义构造函数
People.new = function(name)
local ins = clone(People)
ins.name = name;
return ins
end --定义方法
People.talk = function(self)
print(self.name.." talk.")
end --定义方法2
People.talk2 = function(self)
print(self.name.." talk2.")
end --创建 People 的实例
local p = People.new("Li Lei")
p.talk(p)--people talk.
p:talk()--使用 : 号可以省略 self 的填写, 效果和上一行一致 --下面是关于继承的实现 --将 origin 表的值复制到 dist 表中
function copy(origin, dist)
for key, var in pairs(origin) do
dist[key] = var
end
end Man = {}--定义表 Man, 继承自 People Man.new = function(name)
local ins = People.new(name)--创建继承的类, 这样就拥有了继承类的所有属性
copy(Man, ins)--将 Man 的属性附加到实例
return ins
end --定义方法
Man.say = function()
print("Man say!")
end --重写方法
Man.talk2 = function(self)
print(self.name.." talk2. (Man)")
end --创建 Man 的实例
local m = Man.new("Han Meimei")
m:talk()--Han Meimei talk.
m:talk2()--Han Meimei talk2. (Man)
m:say()--Man say!

简单来说,就是先创建父类的实例,然后把新类的所有属性都拷贝到刚创建的实例上即可。

通过函数闭包的实现

我们可以通过在函数内定义函数的方式来实现面向对象的特性:

 --定义方法直接返回表的实例
function People(name)
local ins = {}--创建表 --初始化函数
local function init()
ins.name = name
end --定义方法
ins.talk = function()
print(ins.name.." talk.")
end --定义带参数的方法
ins.talk2 = function(content)
print(ins.name.." talk "..content..".")
end -- self 可以省去了, 因为可以通过闭包的特性获取到, 或者如果要统一 : 符号可以这么写
ins.talk3 = function(self, content)
print(self.name.." talk "..content..".")
end init()--调用初始化函数 return ins
end --创建对象
p = People("Li Lei")
p:talk()--没有参数可以这么写
p.talk() --p:talk2("hello")--没有 self 参数不能这么调用了
p.talk2("hello") p:talk3("hello 2")
p.talk3(p, "hello 2")

这种写法的运行效率略低于上一种方法,但是却更加清晰,在实际使用时推荐使用该方法来编写类。

继承

使用这种写法也可以实现类的继承,如下:

 --定义方法直接返回表的实例
function People(name)
local ins = {}--创建表 --初始化函数
local function init()
ins.name = name
end --定义方法
ins.talk = function()
print(ins.name.." talk.")
end --定义带参数的方法
ins.talk2 = function(content)
print(ins.name.." talk "..content..".")
end -- self 可以省去了, 因为可以通过闭包的特性获取到, 或者如果要统一 : 符号可以这么写
ins.talk3 = function(self, content)
print(self.name.." talk "..content..".")
end init()--调用初始化函数 return ins
end --下面是继承的代码 function Man(name)
local ins = People(name)--关键: 创建一个 People 类的实例 --添加新项目
ins.say = function()
print(ins.name.." say.")
end --覆写老方法
ins.talk = function()
print(ins.name.." talk. (Man)")
end return ins
end m = Man("Han Meimei")
m.say()--Han Meimei say.
m.talk()--Han Meimei talk. (Man)

其实就是创建父类的对象后添加新的属性,比较容易理解。

Lua学习笔记(五):面向对象的实现的更多相关文章

  1. Lua学习笔记:面向对象

    Lua学习笔记:面向对象 https://blog.csdn.net/liutianshx2012/article/details/41921077 Lua 中只存在表(Table)这么唯一一种数据结 ...

  2. [转]LUA 学习笔记

    Lua 学习笔记 入门级 一.环境配置 方式一: 1.资源下载http://www.lua.org/download.html 2.用src中的源码创建了一个工程,注释调luac.c中main函数,生 ...

  3. Java学习笔记之---面向对象

    Java学习笔记之---面向对象 (一)封装 (1)封装的优点 良好的封装能够减少耦合. 类内部的结构可以自由修改. 可以对成员变量进行更精确的控制. 隐藏信息,实现细节. (2)实现封装的步骤 1. ...

  4. openresty 学习笔记五:访问RabbitMQ消息队列

    openresty 学习笔记五:访问RabbitMQ消息队列 之前通过比较选择,决定采用RabbitMQ这种消息队列来做中间件,目的舒缓是为了让整个架构的瓶颈环节.这里是做具体实施,用lua访问Rab ...

  5. C#可扩展编程之MEF学习笔记(五):MEF高级进阶

    好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...

  6. (转)Qt Model/View 学习笔记 (五)——View 类

    Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...

  7. Lua 学习笔记(一)

    Lua学习笔记 1.lua的优势 a.可扩张性     b.简单     c.高效率     d.和平台无关 2.注释 a.单行注释 --        b.多行注释 --[[  --]] 3.类型和 ...

  8. Lua学习笔记6:C++和Lua的相互调用

        曾经一直用C++写代码.话说近期刚换工作.项目组中的是cocos2dx-lua,各种被虐的非常慘啊有木有.     新建cocos2dx-lua项目.打开class能够发现,事实上就是C++项 ...

  9. java之jvm学习笔记五(实践写自己的类装载器)

    java之jvm学习笔记五(实践写自己的类装载器) 课程源码:http://download.csdn.net/detail/yfqnihao/4866501 前面第三和第四节我们一直在强调一句话,类 ...

  10. UML和模式应用学习笔记-1(面向对象分析和设计)

    UML和模式应用学习笔记-1(面向对象分析和设计) 而只是对情节的记录:此处的用例场景为:游戏者请求掷骰子.系统展示结果:如果骰子的总点数是7,则游戏者赢得游戏,否则为输 (2)定义领域模型:在领域模 ...

随机推荐

  1. iOS开发:在Swift中调用oc库

    先列举这个工程中用到的oc源码库: MBProgressHUD:半透明提示器,Loading动画等 SDWebImage:图片下载和缓存的库 MJRefresh: 下拉刷新,上拉加载 Alamofir ...

  2. web.xml元素介绍

    每一个站的WEB-INF下都有一个web.xml的设定文件,它提供了对我们站台的配置设定.web.xml中定义元素有:◆站台的名称和说明◆针对环境参数(Context)做初始化工作◆Servlet的名 ...

  3. 使用Jquery promise 动态引入js文件

    动态加载一个js得方式很多,如下方式: /** *一般方式加载 */ function normalLoadScript(url) { var node = document.createElemen ...

  4. Asp.Net操作FTP方法

    将用户上传的附件(文件.图片等)通过FTP方式传送到另外一台服务器上,从而缓解服务器压力 1.相关的文章如下: Discuz!NT中远程附件的功能实现[FTP协议] http://www.cnblog ...

  5. 利用dns解析来实现网站的负载均衡

    当网站的访问量大了就会考虑负载均衡,这也是每一个架构师的基本功了,其基本地位就相当于相声里的说学逗唱,活好不好就看这个了 :) 传统的负载均衡思路是单点的,不管你是硬件的还是软件的基本都是这样的原理 ...

  6. poj 1986 Distance Queries(LCA:倍增/离线)

    计算树上的路径长度.input要去查poj 1984. 任意建一棵树,利用树形结构,将问题转化为u,v,lca(u,v)三个点到根的距离.输出d[u]+d[v]-2*d[lca(u,v)]. 倍增求解 ...

  7. 如何使用 orachk 工具

    Oracle RAC 安装完毕后的健壮性是一个令人头疼的问题.之前Oracle为之专门推出了raccheck工具,确实方便了我们这些个苦逼的DBA.现在Oracle在raccheck的基础之上又推出了 ...

  8. spring aop expression简单说明

    <aop:config> <aop:pointcut id="userDAO" expression="execution(public * cn.da ...

  9. oracle 统计语句 与常见函数的归纳(未完待续)

    一.统计语句 1. count count(*)与count(0)语句的区别: count(*)统计所有数量 count(0)统计第一列不为空的 2. 两个统计量的减法 select (select ...

  10. 省市区(县)三级联动代码(js 数据源)

    ylbtech-JavaScript-Utility:省市区(县)三级联动代码(js 数据源) 省市区(县)三级联动代码(js 数据源) 1.A,源代码(Source Code)返回顶部 1.A.1, ...