Lua 极简入门指南(七):面向对象编程_Lua

在很多面向对象的语言中有类(class)的概念,对象是类的实例。Lua 中不存在类的概念。Lua 就像 JavaScript 一样是面向原型的语言(http://en.wikipedia.org/wiki/Prototype-based_programming),这类语言使用一个对象表示一个“类”,其他对象(此类的实例)使用此对象作为原型。我们有两个 table p 和 obj,将 p 设置为 obj 的原型(回顾:http://www.jb51.net/article/56690.htm):

复制代码 代码如下:

setmetatable(obj, {__index = p})

obj 中不存在的操作会在 p 中查找。

看一个详细的例子:

复制代码 代码如下:

Account = {
    -- 默认的 balance 的值
    balance = 0
}
 
function Account:new(o)
    o = o or {}
    -- 设置原型为 Account
    setmetatable(o, self)
    self.__index = self
    return o
end
 
function Account:deposit(v)
    self.balance = self.balance + v
end
 
function Account:withdraw(v)
    if v > self.balance then
        print('insufficient funds')
        return
    end
    self.balance = self.balance - v
end
 
-- 构建 Account 对象,初始 balance 为 100
local a1 = Account:new{balance = 100}
a1:deposit(100)    --> balance == 200
a1:withdraw(100)   --> balance == 100
 
-- 构建 Account 对象,使用默认的 balance
local a2 = Account:new()
a2:deposit(100)    --> balance == 100

在方法定义时使用冒号能够添加一个隐藏的参数 self 给方法,在方法调用时使用冒号能够将调用者作为一个额外的参数传递给此方法,例如:

复制代码 代码如下:

-- 以下两种写法等价
function Account:deposit(v)
function Account.deposit(self, v)
 
-- 以下两种写法等价
a1:deposit(100)
a1.deposit(a1, 100)

self 为方法的调用者。

在 Account 这个例子中,一个小优化是,我们没有必要创建一个额外的 metatable,而直接使用 Account 作为 metatable。

继承

我们通过一个例子来解释 Lua 如何实现继承。假定我们需要子类 SpecialAccount 继承于 Account,SpecialAccount 是可以透支的。

首先构造一个子类:

复制代码 代码如下:

SpecialAccount = Account:new()

这里 SpecialAccount 拥有了(继承了)Account 的 new 方法。那么我们就可以使用 SpecialAccount 构建对象:

复制代码 代码如下:

local sa = SpecialAccount:new{limit = 1000}
sa:withdraw(100)

这里通过 SpecialAccount:new 构造了对象 sa,并且 sa 的 metatable 为 SpecialAccount。执行 sa:withdraw(100),Lua 在 sa 中找不到 withdraw,就会在 SpecialAccount 中找,在 SpecialAccount 中也找不到 withdraw,然后在 Account 中找到 withdraw 并调用它。Account 中的 withdraw 显然不是我们想要的,我们在 SpecialAccount 中重新定义它:

复制代码 代码如下:

function SpecialAccount:withdraw(v)
    if v - self.balance >= self:getLimit() then
        print('insufficient funds')
        return
    end
    self.balance = self.balance - v
end
 
function SpecialAccount:getLimit()
    return self.limit or 0
end

我们再调用 sa:withdraw(100),Lua 先在 SpecialAccount 中找到 withdraw 并调用它。

时间: 2024-10-26 14:50:54

Lua 极简入门指南(七):面向对象编程_Lua的相关文章

Lua极简入门指南(一):函数篇_Lua

Lua 和其他很多语言一样,函数调用时参数列表被包裹在括号中: 复制代码 代码如下: print('Hello World') 特别的情况是,如果函数调用时只有一个参数,并且此参数为字符串 literal(字面量)或者 table 构造器(constructor)时,包裹参数的括号可以省略: 复制代码 代码如下: print 'Hello World' <--> print('Hello World') type{}              <--> type({}) Lua 为

Lua极简入门指南:全局变量_Lua

全局环境 Lua 把全局变量放在一个 table _G 中,这个 table 被叫做全局环境(global environment).打印所有的全局变量名: 复制代码 代码如下: for n in pairs(_G) do print(n) end _ENV(Lua 5.2 开始支持) 对于一个 free name(名字没有绑定任何声明)var 实际上会被转换为 _ENV.var(每个 chunk 中都会存在一个名为 _ENV 的变量): 复制代码 代码如下: v1 = 1 local v2 =

Lua极简入门指南(三): loadfile和错误处理_Lua

编译 Lua 虽然是解释性语言,但 Lua 源码总是被编译为中间形式后再执行. dofile 用于载入并执行一个 Lua 文件,相比之下,loadfile 用于载入一个 Lua 文件,但并不执行,确切的说 loadfile 编译了一个 chunk,并返回此被编译的 chunk(被作为一个函数): 复制代码 代码如下: c = loadfile('./test.lua') c() dofile 可以被实现为: 复制代码 代码如下: function dofile(filename)     loc

Lua极简入门指南(一):基础知识篇_Lua

本文是<Programming in Lua 3rd>读书笔记. Chunks 一个 Chunk 就是一组被执行的语句,例如一个文件或者交互模式下的一行. 标识符(identifiers) 我们应该避免使用以 _ 开头并跟上一个或者多个大写字母的字符串来作标识符,它们被保留作特殊的用途(例如:_VERSION). 注释 单行注释使用 复制代码 代码如下: -- 多行注释使用 复制代码 代码如下: --[[ 和 --]] 类型简介 Lua 存在的数据类型包括: 1.nil.此类型只有一个值 ni

Lua极简入门指南(六):模块_Lua

从用户的角度来看,一个模块能够通过 require 加载并返回一个 table,模块导出的接口都被定义在此 table 中(此 table 被作为一个 namespace).所有的标准库都是模块.标准库被预先加载了,就像这样: 复制代码 代码如下: math = require 'math' string = require 'string' require 函数 使用 require 函数加载模块能够避免多次重复加载模块.加载一个模块: 复制代码 代码如下: require 'modulena

Lua教程(十二):面向对象编程_Lua

Lua中的table就是一种对象,但是如果直接使用仍然会存在大量的问题,见如下代码: 复制代码 代码如下:  Account = {balance = 0}  function Account.withdraw(v)      Account.balance = Account.balance - v  end  --下面是测试调用函数  Account.withdraw(100.00) 在上面的withdraw函数内部依赖了全局变量Account,一旦该变量发生改变,将会导致withdraw不

JavaScript极简入门教程(一):基础篇_javascript技巧

阅读本文需要有其他语言的编程经验. 开始学习之前 大多数的编程语言都存在好的部分和差的部分.本文只讲述 JavaScript 中好的部分,这是因为: 1.仅仅学习好的部分能够缩短学习时间 2.编写的代码更加健壮 3.编写的代码更加易读 4.编写的代码更加易于维护 弱类型和强类型 通常来说,越早的修复错误,为之付出的代价就越小.强类型语言的编译器可以在编译时检查某些错误.而 JavaScript 是一门弱类型语言,其解释器无法检查类型错误,但实践表明: 1.强类型能够避免的错误并不是那些关键性错误

JavaScript极简入门教程(二):对象和函数_javascript技巧

阅读本文需要有其他语言的编程经验. JavaScript 中的简单类型包括: 1.数字 2.字符串 3.布尔(true 和 false) 4.null 5.undefined 此外的其他类型均是对象(我们不要被 typeof 操作符的返回值所迷惑),例如: 1.函数 2.数组 3.正则表达式 4.对象(对象自然也是对象) 对象基础 在 JavaScript 中,对象是属性的集合(对象为关联数组),每个属性包括: 1.属性名,必须为字符串 2.属性值,可以为除了 undefined 之外的任何值

JavaScript极简入门教程(三):数组_javascript技巧

阅读本文需要有其他语言的编程经验. 在 JavaScript 中数组是对象(而非线性分配的内存). 通过数组 literal 来创建数组: 复制代码 代码如下: var empty = []; var numbers = [     'zero', 'one', 'two', 'three', 'four',     'five', 'six', 'seven', 'eight', 'nine' ]; empty[1] // undefined numbers[1] // 'one' empty