本文翻译自 http://lua-users.org/wiki/SimpleLuaClasses ,转载请注明出处。

声明 Lua 类

Lua没有class系统,但是它强有力的元程序设计使定义一个类变的容易。实际上,有很多方式去实现类定义,只是刚开始的时候可能会因为Lua陌生的符号而对Lua的面向对象感到困惑。

这里描述的方法是最普通和灵活的,那就是用元表。table的行为可以通过给它设置一个元表并定义一些元方法来自定义。例如,一个元表有__index函数,那么任何一次对表中元素失败的查询都会传递给__index函数。如果__index是一张表的话,那么会以同样的方式对这张表进行查询。(具体请参考《Lua程序设计第二版》第13章 元表与元方法)

这里是基本的用法:

 1 Account = {}
 2 Account.__index = Account
 3
 4 function Account.create(balance)
 5    local acnt = {}             -- our new object
 6    setmetatable(acnt,Account)  -- make Account handle lookup
 7    acnt.balance = balance      -- initialize our object
 8    return acnt
 9 end
10
11 function Account:withdraw(amount)
12    self.balance = self.balance - amount
13 end
14
15 -- create and use an Account
16 acc = Account.create(1000)
17 acc:withdraw(100)

在这里,acc就是一个只包含 balance 这个元素的表。Lua试图去访问acc中的 withdraw 方法,是访问不到的,但是 acc 有元表并且定义了 __index 方法,那么将会去这个元表(Account)里去访问 withdraw 方法。所以 acc:withdraw(100) 实际上是调用的 Account.withdraw(acc, 100)。(关于 . 和 : 的区别请自行google)。我们也能将 withdraw 直接放到 acc 中去,但是那样太浪费,也不灵活,比如添加一个方法需要去改变 create 方法。

创建 Lua 类

了解了上面类的定义后,下面,我将定义一个 class() 函数去完成所有这些(甚至更多)转换。

 1 Account = class(function(acc,balance)
 2               acc.balance = balance
 3            end)
 4
 5 function Account:withdraw(amount)
 6    self.balance = self.balance - amount
 7 end
 8
 9 -- can create an Account using call notation!
10 acc = Account(1000)
11 acc:withdraw(100)

在这段代码中,提供了一个初始化函数去创建类,一个”构造函数“自动生成了。

同时支持简单的继承,例如,这里基类 Animal 已经定义了,并且声明了一些不同的子类。所有用 class() 函数生成的类都有一个 is_a() 的方法,去唯一确定一个子类。

 1 -- animal.lua
 2
 3 require 'class'
 4
 5 Animal = class(function(a,name)
 6    a.name = name
 7 end)
 8
 9 function Animal:__tostring()
10   return self.name..': '..self:speak()
11 end
12
13 Dog = class(Animal)
14
15 function Dog:speak()
16   return 'bark'
17 end
18
19 Cat = class(Animal, function(c,name,breed)
20          Animal.init(c,name)  -- must init base!
21          c.breed = breed
22       end)
23
24 function Cat:speak()
25   return 'meow'
26 end
27
28 Lion = class(Cat)
29
30 function Lion:speak()
31   return 'roar'
32 end
33
34 fido = Dog('Fido')
35 felix = Cat('Felix','Tabby')
36 leo = Lion('Leo','African')
37
38
39
40 D:\Downloads\func>lua -i animal.lua
41 > = fido,felix,leo
42 Fido: bark      Felix: meow     Leo: roar
43 > = leo:is_a(Animal)
44 true
45 > = leo:is_a(Dog)
46 false
47 > = leo:is_a(Cat)
48 true

所有的 Animal 子类都定义了 __tostring 这个元方法,任何时候 Lua 在操作 Animal 子类的时候有用到 tostring() 这个函数时都会访问到这个元方法。另一方面,它依赖于 speak 函数,这是在子类中没有定义的。所有者就是 C++ 使用者说的抽象基类。具体的派生类,比如 Dog ,自己定义 speak 。需要注意的是,如果派生类有自己的初始化函数,他们必须显式的调用基类的初始化函数。

class()实现

class() 有两个技巧。由于给这个类的元表设置了 __call 方法,它允许你直接调用 class() 去创建一个类,这个主要是用来复制基类中的变量元素。这不是唯一的方式去实现继承,我们也能通过设置 __index 来让子类在访问函数方法的时候来到基类的方法中,这种方式要好得多,精简了类对象的数据。每个派生类要去持有一个 _base 数据,用来实现 is_a 方法。

可以看到,在运行时修改了基类并不会影响到它的子类。

 1 -- class.lua
 2 -- Compatible with Lua 5.1 (not 5.0).
 3 function class(base, init)
 4    local c = {}    -- a new class instance
 5    if not init and type(base) == 'function' then
 6       init = base
 7       base = nil
 8    elseif type(base) == 'table' then
 9     -- our new class is a shallow copy of the base class!
10       for i,v in pairs(base) do
11          c[i] = v
12       end
13       c._base = base
14    end
15    -- the class will be the metatable for all its objects,
16    -- and they will look up their methods in it.
17    c.__index = c
18
19    -- expose a constructor which can be called by <classname>(<args>)
20    local mt = {}
21    mt.__call = function(class_tbl, ...)
22    local obj = {}
23    setmetatable(obj,c)
24    if init then
25       init(obj,...)
26    else
27       -- make sure that any stuff from the base class is initialized!
28       if base and base.init then
29       base.init(obj, ...)
30       end
31    end
32    return obj
33    end
34    c.init = init
35    c.is_a = function(self, klass)
36       local m = getmetatable(self)
37       while m do
38          if m == klass then return true end
39          m = m._base
40       end
41       return false
42    end
43    setmetatable(c, mt)
44    return c
45 end

转载于:https://www.cnblogs.com/sgamerw/p/3444617.html

[译]简单声明Lua类相关推荐

  1. java类体_计算机二级考试Java类之类声明以及类体

    为了方便广大考生更好的复习,帮考网综合整理提供了2012年计算机二级考试java类之类声明以及类体 ,以供各位考生考试复习参考,希望对考生复习有所帮助./计算机三级 2012年计算机等级考试二级jav ...

  2. 你连简单的枚举类都不知道,还敢说自己会Java???滚出我的公司

    枚举类型是Java 5中新增的特性,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性.安全性以及便捷性.当 ...

  3. NET面试题:在C#中如何声明一个类不能被继承

    NET面试题:在C#中如何声明一个类不能被继承 这个问题相对比较简单,应聘者对于这类问题应该不会感到什么难度,但读者更应该掌握的是,为什么要把类型申明为不可继承,这样做的原因是什么.   所涉及的知识 ...

  4. LuaForUnity7.1:Lua“类与对象”

    前文:https://blog.csdn.net/Jaihk662/article/details/95452907(Lua表与元表) 一.Lua的"面对对象" 可以看到题目和标题 ...

  5. c++ using 前置声明_C++ 类的前置声明

    今天在研究C++"接口与实现分离"的时候遇到了一个问题,看似很小,然后背后的东西确值得让人深思!感觉在学习的过程中有太多的为什么,而每一个为什么背后都隐藏着一些原理和目的,所以得多 ...

  6. python简单代码演示效果-演示python如何创建和使用一个简单的元类的代码

    在做工程闲暇时间,将做工程过程比较重要的一些内容备份一下,如下内容段是关于演示python如何创建和使用一个简单的元类的内容,应该能对小伙伴们也有用途. #!/usr/bin/env python # ...

  7. c++ using 前置声明_C++ 类声明 类前置声明范例

    在编写C++程序的时候,偶尔需要用到前置声明(Forward declaration).下面的程序中,带注释的那行就是类B的前置说明.这是必须的,因为类A中用到了类B,而类B的声明出现在类A的后面.如 ...

  8. boost::mpi模块实现一个简单的点类,我们可以构建、添加、比较和 连载

    boost::mpi模块实现一个简单的点类,我们可以构建.添加.比较和 连载 实现功能 C++实现代码 实现功能 (boost::mpi模块实现一个简单的点类,我们可以构建.添加.比较和 连载 C++ ...

  9. DCMTK:简单存储服务类提供者

    DCMTK:简单存储服务类提供者 简单存储服务类提供者 简单存储服务类提供者 #include "dcmtk/config/osconfig.h" #include "d ...

  10. DCMTK:简单存储服务类用户

    DCMTK:简单存储服务类用户 简单存储服务类用户 简单存储服务类用户 #include "dcmtk/config/osconfig.h" #include "dcmt ...

最新文章

  1. VIM命令快速记忆(转自杰哥)
  2. 【转】POJ 1177 Picture(1)
  3. Java三大主流框架概述--(转载)
  4. node怎么解析vue代码_vue中node_modules中第三方模块的修改使用详解
  5. python uwsgi_python nginx+uwsgi+WSGI 处理请求详解
  6. Python 初始篇
  7. 图论及其应用 2009年 期末考试答案 总结
  8. 多个excel合并为一个excel的多个sheet
  9. Arduino - 摇杆模块
  10. 010. 递增子序列
  11. 基于微信小程序的西餐外卖系统的设计与实现NodeJS-计算机毕业设计
  12. Python如何读取Jason格式,变成dataframe
  13. Scroller类源码解析及其应用(一)
  14. 第48章 MDK的编译过程及文件类型全解
  15. 电视PPTV服务器响应异常,pptv出现异常错误怎么解决
  16. [转载]我的老师唐圭璋先生(王兆鹏)_RWERWERWE_96921_新浪博客
  17. Zookeeper异常:FAILED TO WRITE PID与Permission denied
  18. delphi负数变正数_Delphi Format 格式化数字
  19. 有啊百度知道双色球红蓝走势图
  20. iPad内置时钟走快 苹果不允许第三方应用校准

热门文章

  1. php将中文编译成字符串,PHP将汉字字符串转换为数组
  2. ps人像精修照片步骤_PS人像精修
  3. 关于MYSQL ERROR1045 报错的解决办法
  4. [IM002] [Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序...
  5. 持久化/Session和SessionFactory线程非安全和安全/Hibernate的优势
  6. 【第八周】回到起点,从头再来
  7. JSP页面间传递参数
  8. Castle ActiveRecord学习实践(1)入门
  9. IDEA创建maven项目之后无法编写java类
  10. (转)SDL1.2到2.0的迁移指南