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

    分组赛的题……madan原题,考试想不出来真是SB得不行 首先,从大往小加边,每次加边如果成环必然弹出环上最大边 考虑询问[x,y],如果边权在[x,y]的边弹出了小于等于y的边j,说明j不在最小生成 ...

  2. [转] 搜索之双向BFS

    转自:http://www.cppblog.com/Yuan/archive/2011/02/23/140553.aspx 如果目标也已知的话,用双向BFS能很大程度上提高速度. 单向时,是 b^le ...

  3. codevs 1088 神经网络

    bfs.语文题. #include<iostream> #include<cstdio> #include<cstring> #include<algorit ...

  4. shell脚本实例

    备注:一些与传递给shell的参数相关的变量:$# 命令行参数的个数$? 调用命令的返回值$$ 当前进程的进程号$! 最后一个后台命令的进程号$0 命令行的第一个参数,也就是命令名$n 命令行的第n个 ...

  5. jsp防盗链代码

    // 禁止缓存   response.setHeader("Cache-Control", "no-store");   response.setHeader( ...

  6. executeQuery,executeUpdate 和 execute 区别

    http://www.360doc.com/content/14/0315/09/16068204_360719186.shtml http://i-feng.iteye.com/blog/17066 ...

  7. Android应用启动时间及启动日志获取方法

    1. Android应用中,可以使用如下方式进行应用启动时间的查看 2. 启动日志获取方法:

  8. CentOS6.2编译安装codelite5.3

    这两天实验室要求在服务器上安装一个codelite,于是由我对服务器下手,上网看了下悲剧的发现codelite不支持在centos上直接安装,只能编译安装,经过一番折腾之后,成功的装上了,下面是我的安 ...

  9. C++设计模式——建造者模式

    建造者模式 在GOF的<设计模式 可复用面向对象软件的基础>中是这样说的:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 这句话,似懂非懂的.一个复杂对象的创建 ...

  10. 使用logback.xml配置来实现日志文件输出

    转自:http://sungang-1120.iteye.com/blog/2104296 Logback是由log4j创始人设计的又一个开源日志组件.logback当前分成三个模块:logback- ...