Lua 面向对象编程教程

本教程基于 oop.lua 文件,详细介绍 Lua 编程语言中的面向对象编程(OOP)相关知识。虽然 Lua 本身不是一种传统的面向对象编程语言,但它提供了强大的机制来实现面向对象编程范式。

目录

  1. 基本类实现
  2. 继承实现
  3. 多重继承(使用mixin方式)
  4. 私有属性模拟
  5. 类的静态属性和方法
  6. 多态示例
  7. 额外内容:高级OOP特性

1. 基本类实现

在 Lua 中,类通常使用表来实现。通过元表(metatable)机制,可以实现对象的方法调用和属性访问。

基本类结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- 定义一个简单的Person类
Person = {}

-- 构造函数
function Person:new(name, age)
-- 创建新对象,继承Person的所有属性和方法
local obj = {}
setmetatable(obj, self)
self.__index = self

-- 初始化对象属性
obj.name = name
obj.age = age

return obj
end

-- 成员方法
function Person:greet()
print("Hello, my name is " .. self.name .. " and I'm " .. self.age .. " years old.")
end

-- 成员方法
function Person:set_age(new_age)
self.age = new_age
print(self.name .. "'s age is now " .. self.age)
end

创建和使用对象

1
2
3
4
5
6
7
8
9
10
11
-- 创建Person对象
local person1 = Person:new("Alice", 25)
local person2 = Person:new("Bob", 30)

-- 调用对象方法
person1:greet()
person2:greet()

-- 修改对象属性
person1:set_age(26)
person1:greet()

额外示例:使用点语法定义方法

1
2
3
4
5
6
7
-- 也可以使用点语法定义方法,然后在调用时手动传递self
function Person.greet(self)
print("Hello, my name is " .. self.name .. " and I'm " .. self.age .. " years old.")
end

-- 使用点语法调用
Person.greet(person1)

2. 继承实现

Lua 通过元表机制实现继承。子类可以继承父类的所有属性和方法,并且可以重写或添加自己的方法。

基本继承实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-- 定义Student类,继承自Person
Student = Person:new()

-- 学生构造函数
function Student:new(name, age, student_id, major)
-- 调用父类构造函数
local obj = Person:new(name, age)
setmetatable(obj, self)
self.__index = self

-- 初始化Student特有的属性
obj.student_id = student_id
obj.major = major

return obj
end

-- 重写父类方法
function Student:greet()
print("Hello, I'm " .. self.name .. ", a " .. self.major .. " student with ID " .. self.student_id .. ".")
end

-- 添加Student特有的方法
function Student:study(subject)
print(self.name .. " is studying " .. subject .. ".")
end

使用继承

1
2
3
4
5
6
7
8
9
10
11
-- 创建Student对象
local student1 = Student:new("Charlie", 20, "S12345", "Computer Science")
local student2 = Student:new("Diana", 21, "S67890", "Mathematics")

-- 调用方法
student1:greet() -- 调用重写后的方法
student1:study("Lua Programming")
student1:set_age(21) -- 调用继承的方法

student2:greet()
student2:study("Calculus")

额外示例:调用父类方法

1
2
3
4
5
6
7
8
9
10
-- 在子类中调用父类方法
function Student:greet()
-- 调用父类的greet方法
Person.greet(self)
-- 添加子类特有的内容
print("I'm a " .. self.major .. " student with ID " .. self.student_id .. ".")
end

-- 测试
student1:greet()

3. 多重继承(使用mixin方式)

Lua 本身不直接支持多重继承,但可以通过混入(mixin)的方式模拟多重继承。Mixin 是一种将一组方法注入到类中的技术。

实现Mixin

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 定义一个CanSwim mixin
CanSwim = {
swim = function(self)
print(self.name .. " is swimming.")
end
}

-- 定义一个CanFly mixin
CanFly = {
fly = function(self)
print(self.name .. " is flying.")
end
}

使用Mixin实现多重继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
-- 定义Bird类,继承Person并混合CanFly
Bird = Person:new()

function Bird:new(name, age, wingspan)
local obj = Person:new(name, age)
setmetatable(obj, self)
self.__index = self

-- 添加CanFly方法
for k, v in pairs(CanFly) do
if not obj[k] then
obj[k] = v
end
end

obj.wingspan = wingspan
return obj
end

function Bird:greet()
print("Chirp! I'm " .. self.name .. " with a " .. self.wingspan .. "cm wingspan.")
end

-- 定义Duck类,继承Bird并混合CanSwim
Duck = Bird:new()

function Duck:new(name, age, wingspan)
local obj = Bird:new(name, age, wingspan)
setmetatable(obj, self)
self.__index = self

-- 添加CanSwim方法
for k, v in pairs(CanSwim) do
if not obj[k] then
obj[k] = v
end
end

return obj
end

function Duck:greet()
print("Quack! I'm " .. self.name .. ".")
end

使用多重继承

1
2
3
4
5
6
-- 创建Duck对象
local duck = Duck:new("Donald", 5, 60)
duck:greet()
duck:fly() -- 来自CanFly mixin
duck:swim() -- 来自CanSwim mixin
duck:set_age(6) -- 继承自Person的方法

额外示例:Mixin管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
-- 创建一个Mixin管理器
local MixinManager = {
mixins = {}
}

function MixinManager:register_mixin(name, mixin)
self.mixins[name] = mixin
end

function MixinManager:apply_mixin(obj, mixin_name)
local mixin = self.mixins[mixin_name]
if mixin then
for k, v in pairs(mixin) do
if not obj[k] then
obj[k] = v
end
end
return true
end
return false
end

-- 使用Mixin管理器
local mm = MixinManager:new()
mm:register_mixin("CanSwim", CanSwim)
mm:register_mixin("CanFly", CanFly)

-- 应用多个mixin
local super_creature = Person:new("Super Creature", 100)
mm:apply_mixin(super_creature, "CanSwim")
mm:apply_mixin(super_creature, "CanFly")

super_creature:swim()
super_creature:fly()

4. 私有属性模拟

Lua 没有内置的私有属性机制,但可以通过闭包(closure)来模拟私有属性。

使用闭包实现私有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-- 使用闭包模拟私有属性
function create_private_person(name, age)
-- 私有属性
local private_name = name
local private_age = age

-- 公共接口
local obj = {}

obj.get_name = function()
return private_name
end

obj.get_age = function()
return private_age
end

obj.set_age = function(new_age)
if new_age > 0 then
private_age = new_age
return true
end
return false
end

obj.greet = function()
print("Hello, my name is " .. private_name .. " and I'm " .. private_age .. " years old.")
end

return obj
end

使用私有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 创建带有私有属性的person对象
local private_person = create_private_person("Eve", 35)
private_person:greet()
print("Name:", private_person.get_name())
print("Age:", private_person.get_age())

-- 尝试直接访问私有属性(会失败)
print("Can access private_name directly?", private_person.private_name) -- nil

-- 使用公共方法修改年龄
private_person.set_age(36)
private_person:greet()

-- 尝试设置无效年龄
local success = private_person.set_age(-5)
print("Setting invalid age succeeded?", success)

额外示例:混合公有和私有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- 混合公有和私有属性
function create_mixed_person(name, age)
-- 私有属性
local private_age = age

-- 公共接口
local obj = {
name = name -- 公有属性
}

obj.get_age = function()
return private_age
end

obj.set_age = function(new_age)
if new_age > 0 then
private_age = new_age
return true
end
return false
end

obj.greet = function()
print("Hello, my name is " .. obj.name .. " and I'm " .. private_age .. " years old.")
end

return obj
end

local mixed_person = create_mixed_person("Frank", 40)
mixed_person:greet()
print("Public name:", mixed_person.name) -- 可以直接访问
print("Private age (via getter):", mixed_person.get_age()) -- 只能通过 getter 访问

5. 类的静态属性和方法

静态属性和方法属于类本身,而不是类的实例。在 Lua 中,可以直接在类表上定义静态属性和方法。

实现静态属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- 定义一个带有静态属性和方法的类
Counter = {
count = 0, -- 静态属性
instances = {}
}

function Counter:new()
local obj = {}
setmetatable(obj, self)
self.__index = self

-- 增加实例计数
self.count = self.count + 1
table.insert(self.instances, obj)

return obj
end

-- 静态方法
function Counter.get_total_instances()
return Counter.count
end

-- 静态方法
function Counter.reset()
Counter.count = 0
Counter.instances = {}
print("Counter reset.")
end

使用静态属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
-- 创建Counter实例
local counter1 = Counter:new()
local counter2 = Counter:new()
local counter3 = Counter:new()

-- 访问静态属性和方法
print("Total Counter instances:", Counter.get_total_instances()) -- 调用静态方法
print("Static count attribute:", Counter.count) -- 访问静态属性

-- 重置计数器
Counter.reset()
print("Total Counter instances after reset:", Counter.get_total_instances())

额外示例:静态方法的另一种实现

1
2
3
4
5
6
7
8
-- 使用冒号语法定义静态方法
function Counter:static_method()
-- 注意:这里的 self 指向 Counter 类本身
print("Static method called, count is:", self.count)
end

-- 调用静态方法
Counter:static_method()

6. 多态示例

多态是面向对象编程的重要特性,允许不同类型的对象对相同的消息做出不同的响应。在 Lua 中,可以通过方法重写实现多态。

定义抽象基类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 定义一个接口类
Shape = {}

function Shape:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end

-- 抽象方法(应该被子类重写)
function Shape:area()
error("Abstract method 'area' must be implemented by subclass")
end

定义子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
-- Circle类
Circle = Shape:new()

function Circle:new(radius)
local obj = Shape:new()
setmetatable(obj, self)
self.__index = self
obj.radius = radius
return obj
end

function Circle:area()
return math.pi * self.radius * self.radius
end

-- Rectangle类
Rectangle = Shape:new()

function Rectangle:new(width, height)
local obj = Shape:new()
setmetatable(obj, self)
self.__index = self
obj.width = width
obj.height = height
return obj
end

function Rectangle:area()
return self.width * self.height
end

-- Square类
Square = Rectangle:new()

function Square:new(side)
local obj = Rectangle:new(side, side)
setmetatable(obj, self)
self.__index = self
return obj
end

使用多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 多态函数,接受任何Shape类型
function print_area(shape)
print("Area:", shape:area())
end

-- 创建不同形状的对象
local circle = Circle:new(5)
local rectangle = Rectangle:new(4, 6)
local square = Square:new(5)

-- 调用多态函数
print("Circle area:")
print_area(circle)

print("Rectangle area:")
print_area(rectangle)

print("Square area:")
print_area(square)

额外示例:多态的实际应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 多态的实际应用:图形渲染系统
local Renderer = {
render = function(self, shape)
print("Rendering shape with area:", shape:area())
-- 这里可以根据不同的形状类型执行不同的渲染逻辑
if shape.radius then
print("Rendering a circle with radius:", shape.radius)
elseif shape.width and shape.height then
print("Rendering a rectangle with width:", shape.width, "and height:", shape.height)
end
end
}

local renderer = Renderer:new()
renderer:render(circle)
renderer:render(rectangle)
renderer:render(square)

7. 额外内容:高级OOP特性

属性访问控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
-- 使用元表实现属性访问控制
local Person = {}

function Person:new(name, age)
local obj = {
_name = name, -- 约定以下划线开头的属性为私有
_age = age
}

local mt = {
__index = function(t, k)
-- 公共属性直接返回
if k == "name" then
return t._name
end
-- 其他属性通过方法访问
return Person[k]
end,

__newindex = function(t, k, v)
-- 防止直接修改私有属性
if k == "name" or k == "age" then
error("Cannot modify private property directly, use set_age method")
end
rawset(t, k, v)
end
}

setmetatable(obj, mt)
return obj
end

function Person:set_age(new_age)
if new_age > 0 then
self._age = new_age
return true
end
return false
end

function Person:greet()
print("Hello, my name is " .. self._name .. " and I'm " .. self._age .. " years old.")
end

local person = Person:new("Grace", 28)
print("Name:", person.name) -- 可以访问
-- person.name = "New Name" -- 会报错
person:set_age(29)
person:greet()

链式方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
-- 实现链式方法调用
local Person = {}

function Person:new(name, age)
local obj = {}
setmetatable(obj, self)
self.__index = self
obj.name = name
obj.age = age
return obj
end

function Person:set_name(name)
self.name = name
return self -- 返回self以支持链式调用
end

function Person:set_age(age)
self.age = age
return self -- 返回self以支持链式调用
end

function Person:greet()
print("Hello, my name is " .. self.name .. " and I'm " .. self.age .. " years old.")
return self -- 返回self以支持链式调用
end

-- 使用链式调用
local person = Person:new("Henry", 30)
person:set_name("Harry"):set_age(31):greet()

类的模块化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- 将类封装到模块中
-- person.lua
local Person = {}

function Person:new(name, age)
local obj = {}
setmetatable(obj, self)
self.__index = self
obj.name = name
obj.age = age
return obj
end

function Person:greet()
print("Hello, my name is " .. self.name .. " and I'm " .. self.age .. " years old.")
end

return Person

-- 使用模块
local Person = require("person")
local person = Person:new("Ivy", 25)
person:greet()

运行示例

要运行 oop.lua 文件,只需在命令行中执行:

1
lua oop.lua

运行结果将显示各个部分的输出,包括类的创建、继承、多重继承、私有属性、静态方法和多态的示例。

总结

本教程介绍了 Lua 编程语言中的面向对象编程相关知识,包括:

  • 基本类实现:使用表和元表创建类和对象
  • 继承实现:通过元表机制实现类的继承
  • 多重继承:使用mixin方式模拟多重继承
  • 私有属性:使用闭包模拟私有属性
  • 静态属性和方法:在类表上定义静态成员
  • 多态:通过方法重写实现多态
  • 高级特性:属性访问控制、链式方法调用和类的模块化

虽然 Lua 不是一种传统的面向对象编程语言,但通过上述技术,可以实现功能完备的面向对象编程范式。Lua 的面向对象实现方式灵活多样,可以根据具体需求选择最合适的方法。

oop.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
-- Lua面向对象编程示例
-- print语句使用英语,注释使用中文

-- 1. 基本类实现
print("=== Basic Class Implementation ===")

-- 定义一个简单的Person类
Person = {}

-- 构造函数
function Person:new(name, age)
-- 创建新对象,继承Person的所有属性和方法
local obj = {}
setmetatable(obj, self)
self.__index = self

-- 初始化对象属性
obj.name = name
obj.age = age

return obj
end

-- 成员方法
function Person:greet()
print("Hello, my name is " .. self.name .. " and I'm " .. self.age .. " years old.")
end

-- 成员方法
function Person:set_age(new_age)
self.age = new_age
print(self.name .. "'s age is now " .. self.age)
end

-- 创建Person对象
local person1 = Person:new("Alice", 25)
local person2 = Person:new("Bob", 30)

-- 调用对象方法
person1:greet()
person2:greet()

-- 修改对象属性
person1:set_age(26)
person1:greet()

-- 2. 继承实现
print("\n=== Inheritance Implementation ===")

-- 定义Student类,继承自Person
Student = Person:new()

-- 学生构造函数
function Student:new(name, age, student_id, major)
-- 调用父类构造函数
local obj = Person:new(name, age)
setmetatable(obj, self)
self.__index = self

-- 初始化Student特有的属性
obj.student_id = student_id
obj.major = major

return obj
end

-- 重写父类方法
function Student:greet()
print("Hello, I'm " .. self.name .. ", a " .. self.major .. " student with ID " .. self.student_id .. ".")
end

-- 添加Student特有的方法
function Student:study(subject)
print(self.name .. " is studying " .. subject .. ".")
end

-- 创建Student对象
local student1 = Student:new("Charlie", 20, "S12345", "Computer Science")
local student2 = Student:new("Diana", 21, "S67890", "Mathematics")

-- 调用方法
student1:greet() -- 调用重写后的方法
student1:study("Lua Programming")
student1:set_age(21) -- 调用继承的方法

student2:greet()
student2:study("Calculus")

-- 3. 多重继承(使用mixin方式)
print("\n=== Multiple Inheritance (Mixin Approach) ===")

-- 定义一个CanSwim mixin
CanSwim = {
swim = function(self)
print(self.name .. " is swimming.")
end
}

-- 定义一个CanFly mixin
CanFly = {
fly = function(self)
print(self.name .. " is flying.")
end
}

-- 定义Bird类,继承Person并混合CanFly
Bird = Person:new()

function Bird:new(name, age, wingspan)
local obj = Person:new(name, age)
setmetatable(obj, self)
self.__index = self

-- 添加CanFly方法
for k, v in pairs(CanFly) do
if not obj[k] then
obj[k] = v
end
end

obj.wingspan = wingspan
return obj
end

function Bird:greet()
print("Chirp! I'm " .. self.name .. " with a " .. self.wingspan .. "cm wingspan.")
end

-- 定义Duck类,继承Bird并混合CanSwim
Duck = Bird:new()

function Duck:new(name, age, wingspan)
local obj = Bird:new(name, age, wingspan)
setmetatable(obj, self)
self.__index = self

-- 添加CanSwim方法
for k, v in pairs(CanSwim) do
if not obj[k] then
obj[k] = v
end
end

return obj
end

function Duck:greet()
print("Quack! I'm " .. self.name .. ".")
end

-- 创建Duck对象
local duck = Duck:new("Donald", 5, 60)
duck:greet()
duck:fly()
duck:swim()
duck:set_age(6) -- 继承自Person的方法

-- 4. 私有属性模拟
print("\n=== Private Property Simulation ===")

-- 使用闭包模拟私有属性
function create_private_person(name, age)
-- 私有属性
local private_name = name
local private_age = age

-- 公共接口
local obj = {}

obj.get_name = function()
return private_name
end

obj.get_age = function()
return private_age
end

obj.set_age = function(new_age)
if new_age > 0 then
private_age = new_age
return true
end
return false
end

obj.greet = function()
print("Hello, my name is " .. private_name .. " and I'm " .. private_age .. " years old.")
end

return obj
end

-- 创建带有私有属性的person对象
local private_person = create_private_person("Eve", 35)
private_person:greet()
print("Name:", private_person.get_name())
print("Age:", private_person.get_age())

-- 尝试直接访问私有属性(会失败)
print("Can access private_name directly?", private_person.private_name) -- nil

-- 使用公共方法修改年龄
private_person.set_age(36)
private_person:greet()

-- 尝试设置无效年龄
local success = private_person.set_age(-5)
print("Setting invalid age succeeded?", success)

-- 5. 类的静态属性和方法
print("\n=== Class Static Properties and Methods ===")

-- 定义一个带有静态属性和方法的类
Counter = {
count = 0, -- 静态属性
instances = {}
}

function Counter:new()
local obj = {}
setmetatable(obj, self)
self.__index = self

-- 增加实例计数
self.count = self.count + 1
table.insert(self.instances, obj)

return obj
end

-- 静态方法
function Counter.get_total_instances()
return Counter.count
end

-- 静态方法
function Counter.reset()
Counter.count = 0
Counter.instances = {}
print("Counter reset.")
end

-- 创建Counter实例
local counter1 = Counter:new()
local counter2 = Counter:new()
local counter3 = Counter:new()

-- 访问静态属性和方法
print("Total Counter instances:", Counter.get_total_instances()) -- 调用静态方法
print("Static count attribute:", Counter.count) -- 访问静态属性

-- 重置计数器
Counter.reset()
print("Total Counter instances after reset:", Counter.get_total_instances())

-- 6. 多态示例
print("\n=== Polymorphism Example ===")

-- 定义一个接口类
Shape = {}

function Shape:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end

-- 抽象方法(应该被子类重写)
function Shape:area()
error("Abstract method 'area' must be implemented by subclass")
end

-- Circle类
Circle = Shape:new()

function Circle:new(radius)
local obj = Shape:new()
setmetatable(obj, self)
self.__index = self
obj.radius = radius
return obj
end

function Circle:area()
return math.pi * self.radius * self.radius
end

-- Rectangle类
Rectangle = Shape:new()

function Rectangle:new(width, height)
local obj = Shape:new()
setmetatable(obj, self)
self.__index = self
obj.width = width
obj.height = height
return obj
end

function Rectangle:area()
return self.width * self.height
end

-- Square类
Square = Rectangle:new()

function Square:new(side)
local obj = Rectangle:new(side, side)
setmetatable(obj, self)
self.__index = self
return obj
end

-- 多态函数,接受任何Shape类型
function print_area(shape)
print("Area:", shape:area())
end

-- 创建不同形状的对象
local circle = Circle:new(5)
local rectangle = Rectangle:new(4, 6)
local square = Square:new(5)

-- 调用多态函数
print("Circle area:")
print_area(circle)

print("Rectangle area:")
print_area(rectangle)

print("Square area:")
print_area(square)