Lua 小白入门教程Lua 小白入门教程
首页
基础教程
实战进阶
编程指南
首页
基础教程
实战进阶
编程指南
  • 实战进阶

    • 🚀 实战进阶
    • 第1章 - 模块与包管理
    • 第2章 - 文件 I/O 操作
    • 第3章 - 错误处理
    • 第4章 - 面向对象编程
    • 第5章 - SpringBoot + Lua 整合实战 ⭐
    • 第6章 - Redis + Lua 脚本 ⭐
    • 第7章 - OpenResty 入门
    • 第8章 - 最佳实践与常见坑

第4章 - 面向对象编程

嗨,朋友!我是长安。

作为 Java 程序员,面向对象是你的老本行了。但 Lua 没有 class 关键字!那怎么实现类、继承、多态呢?答案是:Table + 元表(Metatable)。

🤔 元表是什么?

元表(Metatable)是 Lua 的一个核心机制,它允许你改变 Table 的行为。简单来说,元表就是一个"行为配置表",告诉 Lua 当你对一个 Table 做某些操作时该怎么处理。

local t = {}
local mt = {}    -- 这是一个元表

-- 给 t 设置元表
setmetatable(t, mt)

-- 获取元表
print(getmetatable(t) == mt)    -- true

🔑 __index 元方法

__index 是最重要的元方法,它决定了当你访问一个 Table 中不存在的键时,该去哪里找:

local defaults = {
    color = "red",
    size = 10,
    visible = true
}

local mt = {
    __index = defaults    -- 找不到就去 defaults 里找
}

local button = setmetatable({}, mt)
button.text = "点击我"

print(button.text)      -- 点击我(自己有,直接返回)
print(button.color)     -- red(自己没有,去 defaults 找)
print(button.size)      -- 10
print(button.visible)   -- true

Java 对比

这就像 Java 的继承!子类访问属性时,先找自己的,找不到就去父类找。__index 就是 Lua 的"继承链"。

🏗️ 用 Table 实现类

基本类定义

-- 定义一个 "Animal" 类
local Animal = {}
Animal.__index = Animal    -- 关键!让实例能找到类方法

-- 构造函数(类似 Java 的 constructor)
function Animal.new(name, sound)
    local self = setmetatable({}, Animal)
    self.name = name
    self.sound = sound
    return self
end

-- 方法
function Animal:speak()
    print(self.name .. " says: " .. self.sound)
end

function Animal:getName()
    return self.name
end

-- 使用
local dog = Animal.new("旺财", "汪汪汪")
local cat = Animal.new("咪咪", "喵喵喵")

dog:speak()     -- 旺财 says: 汪汪汪
cat:speak()     -- 咪咪 says: 喵喵喵

Java 等价

public class Animal {
    private String name;
    private String sound;
    
    public Animal(String name, String sound) {
        this.name = name;
        this.sound = sound;
    }
    
    public void speak() {
        System.out.println(name + " says: " + sound);
    }
    
    public String getName() {
        return name;
    }
}

Animal dog = new Animal("旺财", "汪汪汪");
Animal cat = new Animal("咪咪", "喵喵喵");
dog.speak();    // 旺财 says: 汪汪汪

原理图解

dog (实例 Table)              Animal (类 Table)
┌──────────────────┐         ┌──────────────────┐
│ name = "旺财"    │         │ __index = Animal  │
│ sound = "汪汪汪" │         │ new = function    │
│ [metatable] ─────┼────────►│ speak = function  │
└──────────────────┘         │ getName = function│
                             └──────────────────┘

dog:speak()
1. dog 中找 speak → 没有
2. 通过元表的 __index 找到 Animal
3. Animal 中找到 speak → 调用!

🧬 继承

-- 父类:Animal
local Animal = {}
Animal.__index = Animal

function Animal.new(name, sound)
    local self = setmetatable({}, Animal)
    self.name = name
    self.sound = sound
    return self
end

function Animal:speak()
    print(self.name .. " says: " .. self.sound)
end

function Animal:toString()
    return "Animal: " .. self.name
end

-- 子类:Dog(继承 Animal)
local Dog = setmetatable({}, {__index = Animal})    -- Dog 继承 Animal
Dog.__index = Dog

function Dog.new(name, breed)
    local self = Animal.new(name, "汪汪汪")         -- 调用父类构造函数
    setmetatable(self, Dog)                          -- 设置为 Dog 实例
    self.breed = breed
    return self
end

-- 子类新增方法
function Dog:fetch(item)
    print(self.name .. " 捡回了 " .. item)
end

-- 重写父类方法(多态)
function Dog:toString()
    return "Dog: " .. self.name .. " (" .. self.breed .. ")"
end

-- 使用
local myDog = Dog.new("旺财", "金毛")

myDog:speak()           -- 旺财 says: 汪汪汪(继承的方法)
myDog:fetch("飞盘")     -- 旺财 捡回了 飞盘(自己的方法)
print(myDog:toString()) -- Dog: 旺财 (金毛)(重写的方法)

Java 等价

public class Dog extends Animal {
    private String breed;
    
    public Dog(String name, String breed) {
        super(name, "汪汪汪");    // 调用父类构造函数
        this.breed = breed;
    }
    
    public void fetch(String item) {
        System.out.println(getName() + " 捡回了 " + item);
    }
    
    @Override
    public String toString() {
        return "Dog: " + getName() + " (" + breed + ")";
    }
}

📊 常用元方法一览

元方法触发条件Java 对比
__index访问不存在的键继承/属性查找
__newindex给不存在的键赋值setter
__tostringtostring() 调用toString()
__add+ 运算运算符重载
__sub- 运算运算符重载
__mul* 运算运算符重载
__eq== 比较equals()
__lt< 比较compareTo()
__len# 运算size()
__call当作函数调用Callable
__concat.. 拼接—

元方法示例

-- 实现一个向量类(演示运算符重载)
local Vector = {}
Vector.__index = Vector

function Vector.new(x, y)
    return setmetatable({x = x, y = y}, Vector)
end

-- 重载 + 运算符
function Vector.__add(a, b)
    return Vector.new(a.x + b.x, a.y + b.y)
end

-- 重载 tostring
function Vector.__tostring(v)
    return string.format("(%d, %d)", v.x, v.y)
end

-- 重载 == 运算符
function Vector.__eq(a, b)
    return a.x == b.x and a.y == b.y
end

-- 使用
local v1 = Vector.new(1, 2)
local v2 = Vector.new(3, 4)
local v3 = v1 + v2              -- 触发 __add

print(tostring(v3))              -- (4, 6)
print(v1 == Vector.new(1, 2))   -- true

🔧 封装:getter/setter

-- 用 __newindex 实现属性校验
local Person = {}
Person.__index = Person

function Person.new(name, age)
    local self = setmetatable({}, Person)
    self._name = name    -- 用下划线前缀表示"私有"
    self._age = age
    return self
end

function Person:getName() return self._name end
function Person:getAge() return self._age end

function Person:setAge(age)
    if age < 0 or age > 150 then
        error("年龄不合法: " .. age)
    end
    self._age = age
end

-- 使用
local p = Person.new("长安", 25)
print(p:getName())      -- 长安
print(p:getAge())       -- 25

p:setAge(26)
print(p:getAge())       -- 26

-- p:setAge(-1)         -- 错误:年龄不合法: -1

📝 小结

  • Lua 用 Table + 元表 实现面向对象
  • __index 元方法是继承的核心——实例找不到属性就去类(元表)里找
  • 构造函数用 setmetatable({}, ClassName) 创建实例
  • 方法用冒号定义和调用,自动传入 self
  • 元方法可以重载运算符(__add、__eq、__tostring 等)
  • 虽然没有 private 关键字,但可以用闭包或命名约定(下划线前缀)实现封装

➡️ 下一步

面向对象搞定了!下一章是重头戏——SpringBoot + Lua 整合实战,教你在 Java 项目中使用 Lua!

💪 练习题

  1. 创建一个 Rectangle 类,包含 width、height 属性和 area() 方法。
  2. 创建一个 Square 类继承 Rectangle,构造函数只接收一个边长参数。
  3. 给 Rectangle 添加 __tostring 元方法。
  4. 解释 __index 元方法的作用。

答案提示

  1. function Rectangle.new(w,h) return setmetatable({w=w,h=h}, Rectangle) end + function Rectangle:area() return self.w*self.h end
  2. function Square.new(s) return Rectangle.new(s,s) end + 设置 Square 的元表指向 Rectangle
  3. function Rectangle.__tostring(r) return string.format("Rect(%dx%d)", r.w, r.h) end
  4. 当访问 Table 中不存在的键时,Lua 会去元表的 __index 指向的 Table(或函数)中查找
最近更新: 2026/2/27 17:54
Contributors: 王长安
Prev
第3章 - 错误处理
Next
第5章 - SpringBoot + Lua 整合实战 ⭐