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. bzoj3672

    感觉是noi2014中最有价值的一道题了 我们先考虑链上这个问题怎么做…… 如果没限制,那就是SB的斜率优化 我们可以得到这个式子(f[j]-f[k])/(s[j]-s[k])<p[i] 点横坐 ...

  2. web交互方式

    轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接. 优点:后端程序编写比较容易. 缺点:请求中有大半是无用,浪费带宽和服务器资源. 实例:适于小型应用. 长轮询:客 ...

  3. poj 1364 King(差分约束)

    题意(真坑):傻国王只会求和,以及比较大小.阴谋家们想推翻他,于是想坑他,上交了一串长度为n的序列a[1],a[2]...a[n],国王作出m条形如(a[si]+a[si+1]+...+a[si+ni ...

  4. https实现安全传输的流程

    HTTPS简介 HTTPS其实是有两部分组成:HTTP + SSL / TLS,也就是在HTTP上又加了一层处理加密信息的模块.服务端和客户端的信息传输都会通过TLS进行加密,所以传输的数据都是加密后 ...

  5. Android 如何直播RTMP流

    在android上,视频/音频流直播是极少有人关注的一部分.每当我们讨论流媒体,RTMP(Real Time Messaging Protocol)是不可或缺的.RTMP是一个基本的视频/音频直播流协 ...

  6. 连接Excel时出现未指定的错误

    使用  strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + filepath + ";Extended ...

  7. hihocoder 1233 Boxes

    题意:类汉诺塔的一个东西……移动规则与汉诺塔一样,但初始状态为题目中给出的每根棍上一个盘子,目标状态为盘子在棍上按大小顺序排列,盘子只能在相邻的棍儿上移动. 解法:广搜并打表记录从目标状态到所有可能的 ...

  8. TOAD FOR MYSQL 进行数据插入时乱码的解决办法---MariaDB 5.5

    最近使用mysql是发现插入的数据乱码,几经周折终于找到的解决方法,特作备忘. 开始有将mysql的字符集全部设置成utf8,如下: SHOW VARIABLES LIKE 'character_se ...

  9. Windows 窗体最小化和隐藏的区别及恢复

    应用程序有托盘图标,窗体最小化或者隐藏窗体时,点击托盘图标需要恢复窗体显示,并且恢复之前的现实状体,正常大小或者最大化显示.例如:最大化显示窗体时,点击最小化按钮,窗体最小化,点击托盘图标将最大化显示 ...

  10. C++的笔记学习第一篇,认识C++

    在一个类中包含两种成员: 数据和函数,分别称为C++数据成员和成员函数. 关于类: 类是C++新增加的重要数据类型,有了类,就就可以实现面向对象程序设计方法中的封装.信息隐蔽.继承.派生.多态等功能. ...