Lua实现类与继承的实例详解

Lua中没有类的概念,但可以通过table、函数以及一些语言特性来模拟类。table的值可以为函数。看下面这段代码:

LogUtil = {msg_prefix = string.format("%s ", os.date("%H:%M:%S", os.time())) }
LogUtil.write = function(msg)
    print(LogUtil.msg_prefix .. msg .. "\r\n")
end
 
LogUtil.write("This is a Notice Info")

定义一个日志table,table有一个名为msg_prefix的键以及一个名为write的键。msg_prefix指向一个字符串,write指向一个方法,然后调用了write方法。这里看起来就是一个table的操作,但也可以换个角度来看:定义了一个日志类,类里有一个静态成员属性msg_prefix以及一个静态成员方法write。并且调用了write方法。

这也就是Lua实现类的基础。

上面代码中,LogUtil为全局变量。如果重写了它的值将会导致方法不可用。所以可以用一个额外的参数来传入对象,这个参数经常为self或this,好比Python类方法中也需要显示的传入self对象。如:

LogUtil = {msg_prefix = string.format("%s ", os.date("%H:%M:%S", os.time())) }
 
LogUtil.write = function(self, msg)
    print(self.msg_prefix .. msg .. "\r\n")
end
 
LogA = LogUtil
LogUtil = nil
LogA.write(LogA, "This is a LogA Info")

但这样子调用的时候也需要传入self参数,不过,Lua也提供了通过使用冒号操作符来隐藏这个参数的声明。看下面,

LogUtil = {msg_prefix = string.format("%s ", os.date("%H:%M:%S", os.time())) }
 
function LogUtil:write(msg)
    print(self.msg_prefix .. msg .. "\r\n")
end
 
LogA = LogUtil
LogA:write("This is a LogA Info")

类定义还是被调用,通过冒号方式的调用都会自动将类作为self参数传入,而省去了主动传入的麻烦。还有, function提在前面了,可以看到这里更有函数的味道了。上面两段代码是等价的,归纳下就是:

function class.a.b:c = function(param) body end

等价于

class.a.b.c = function(self, param) body end

可能,会被上面的点、冒号、self搞混,其实可以统一使用冒号的方式来定义与调用类方法。

到目前为止还只是一些属性和方法的集合,并没有实例化类,属性的改变会导致全局的变化。接下来看如何实现多个互不影响的类的实例。

类的实例

在Lua中有一个神奇的东西叫做metatable(元表)。metatable也是键值对。当访问table中一个不存在的属性时就会触发一些事件。比如当设置了元方法__index时 表示当调用当前table中不存在的属性或方法时会到元方法对应的table中去找,从而实现继承的效果。

setmetatable(a, {__index = b})

这样,对象a调用任何不存在的成员都会到对象b中查找。术语上,可以将b看作类,a看作对象。

LogUtil = {msg_prefix = string.format("%s ", os.date("%H:%M:%S", os.time())) }
 
function LogUtil:write(msg)
    print(self.msg_prefix .. msg .. "\r\n")
end
 
-- 创建实例化的new方法,名字可以任意制定。
function LogUtil:new(o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
end
 
LogA = LogUtil:new()
LogA.msg_prefix = ""
LogA:write("This is a LogA Info")
 
LogB = LogUtil:new{msg_prefix = "Hello."}
LogB:write("This is a LogB Info")
 
--[[
-- output:
This is a LogA Info
 
Hello.This is a LogB Info
]]

可以看到,A和B的操作是互不影响的,相当于对类进行了实例化。有两点需要注意的地方:
1、new方法中的setmetatable(o, self)只是做了一个小小的优化,不需要创建一个额外的表作为LogA或LogB的元表。再简单点说, 元表只是取了LogUtil的一个__index键。对于LogUtil来说,只是多了一个__index键而已。
2、LogB实例化的时候参数为一个table,此时可以省略括号。表示在o中已经设置了msg_prefix,不再需要去LogUtil寻找,达到重写的目的。

当执行LogA:write()时先在LogA中查找,LogA没有此方法,此时会到LogUtil中去找。基本上已经也有了继承的概念。

类的继承

在前面基础上我们调整下调用,LogA为LogUtil的实例,找不到的方法都到LogUtil中去找。如果想要LogB继承自LogA呢?可以直接在new一下LogA。

LogA = LogUtil:new{msg_prefix = 'A:'}
LogA:write("This is a LogA Info")
 
LogB = LogA:new{}
LogB:write("This is a LogB Info")

LogA再执行new方法时,此时的self参数为LogA了,所以相当于LogB继承自LogA。

同样, Lua还可以实现多重继承,成员属性私有等,这些就留待以后去深究了

时间: 2024-10-26 05:58:07

Lua实现类与继承的实例详解的相关文章

PHP类和对象函数实例详解

1. interface_exists.class_exists.method_exists和property_exists:         顾名思义,从以上几个函数的命名便可以猜出几分他们的功能.我想这也是我随着对PHP的深入学习而越来越喜欢这门编程语言的原因了吧.下面先给出他们的原型声明和简短说明,更多的还是直接看例子代码吧. bool interface_exists (string $interface_name [, bool $autoload = true ]) 判断接口是否存在

javascript继承机制实例详解_javascript技巧

本文实例讲述了javascript继承机制.分享给大家供大家参考.具体分析如下: 初学javascript一般很难理解Javascript语言的继承机制它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承. 我花了很多时间,学习这个部分,还做了很多笔记.但是都属于强行记忆,无法从根本上理解

PHP PEAR DB类安装与使用实例详解

在PHP网站开发中,由于其支持各种数据库引擎,如Mysql,Mssql,Pgsql,sqlite等,并对各种数据库系统都提供了不同的函数作为接口,给PHP网站开发者带来很多便利,但同时也带来了平台移植性的问题,随着底层数据库的改变,PHP代码也必须改变.对于这个问题,有各种解决方法,如使用PHP ADODB类,PHP PEAR DB类或者自行编写PHP DB类,将各种数据库的函数操作聚合在一起等,今天和大家分享如何安装使用PHP PEAR DB类,以实现不同数据库的访问功能. 准备工作 1.在使

Python中类型关系和继承关系实例详解

  本文详细介绍了Python中类型关系和继承关系.分享给大家供大家参考.具体分析如下: 如果一个对象A持有另一个对象B的ID,那么检索到A之后就可以检索到B,我们就说存在一个A到B的导航.这种导航关系使得Python中所有对象之间形成了一个复杂的网络结构. Python程序的运行包括: 1. 修改这个网络结构; 2. 执行有副作用的代码对象(code object或者说bytecode,见Python Language Reference 3.2) (副作用是指影响Python虚拟机之外的设备

PHP邮件发送类PHPMailer用法实例详解_php技巧

本文实例讲述了PHP邮件发送类PHPMailer用法,并详细讲述了其具体的操作步骤.分享给大家供大家参考.具体步骤如下: 1.在服务器安装 sendmail sudo apt-get install sendmail 2.启动 sendmail sudo /etc/init.d/sendmail start 3.修改 php.ini [mail function] SMTP = localhost smtp_port = 25 sendmail_from = me@example.com 4.F

CodeIgniter扩展核心类实例详解_php实例

本文实例讲述了CodeIgniter扩展核心类的方法.分享给大家供大家参考,具体如下: CI中对核心类.辅助类和函数的扩展是相当方便的,配置文件中指定了subclass_prefix扩展前缀,默认为MY_,扩展时需要以该配置为前缀,下面整理下扩展方式. 1.扩展核心类 核心类位于system/core下,其中大部分类会在初始化的时候自动加载.扩展核心类的方式有两种:替换核心类和继承核心类. 替换核心类 当application/core目录下存在与system/core同名的文件时会自动替换掉核

PHP文件上传类实例详解_php技巧

本文实例讲述了PHP文件上传类.分享给大家供大家参考,具体如下: 这里演示了FileUpload.class.php文件上传类,其中用到了两个常量,可在网站配置文件中定义: define('ROOT_PATH',dirname(__FILE__)); //网站根目录 define('UPDIR','/uploads/'); //上传主目录 具体代码如下: <?php //上传文件类 class FileUpload { private $error; //错误代码 private $maxsiz

Python文件操作类操作实例详解_python

本文讲述了Python文件操作类的操作实例,详细代码如下: #!/usr/bin/env python #!/usr/bin/env python #coding:utf-8 # Purpose: 文件操作类 #声明一个字符串文本 poem=''' Programming is fun测试 When the work is done if you wanna make your work also fun: use Python! ''' #创建一个file类的实例,模式可以为:只读模式('r'

PHP中Closure类的使用方法及详解_php实例

Closure,匿名函数,又称为Anonymous functions,是php5.3的时候引入的.匿名函数就是没有定义名字的函数.这点牢牢记住就能理解匿名函数的定义了. Closure 类(PHP 5 >= 5.3.0)简介 用于代表 匿名函数 的类. 匿名函数(在 PHP 5.3 中被引入)会产生这个类型的对象,下面我们来看一下PHP Closure类的使用方法及介绍. PHP Closure类之前在PHP预定义接口中介绍过,但它可不是interface哦,它是一个内部的final类.Clo