Menu
CoddyTech

只读表

本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 46 课,共 70 课。

闭包可以实现真正的私有性,但需要在构造函数中定义方法。对于另一种使用场景,还有一种做法:在创建对象后将其设为只读。如果你希望字段可见但不可更改,例如固定点的坐标,这种做法就很有用。

__newindex 元方法会拦截向表赋新值的操作。让它阻止所有赋值操作,就可以冻结一个对象:

local ImmutablePoint = {}

function ImmutablePoint.new(x, y)
    local data = {x = x, y = y}
    
    local proxy = {}
    setmetatable(proxy, {
        __index = data,
        __newindex = function()
            error("Cannot modify immutable object")
        end
    })
    
    return proxy
end

诀窍是使用代理表。实际数据存储在 data 中,而用户与空的 proxy 交互。

读取时,__index 会从 data 中获取值。写入时,则会触发 __newindex,我们会拦截它:

local p = ImmutablePoint.new(10, 20)
print(p.x)  -- 10(可以读取)
p.x = 50    -- 错误:无法修改不可变对象

这种模式适用于配置对象、数学常量,或任何初始化后不应更改的数据。对象仍然完全可读,只是你无法更改它。

challenge icon

挑战

简单

让我们构建一个 ImmutableConfig 类,用来创建只读配置对象!创建后,这些配置对象应允许读取设置,但完全禁止任何修改尝试。

你需要将代码分成两个文件:

  • ImmutableConfig.lua:创建一个能生成不可变配置对象的模块。你的 .new(appName, version, maxUsers) 函数应在内部存储这三个值,然后返回一个代理表,允许读取所有字段,但禁止任何修改。当有人尝试更改值时,应输出 Error: Config is read-only,而不是引发真正的错误。
  • main.lua:让你的 ImmutableConfig 真正发挥作用!读取三个配置值作为输入,创建一个配置对象,然后通过以下操作展示它能正常工作:
    • 输出全部三个原始值
    • 尝试修改 maxUsers 字段(这应触发错误消息)
    • 再次输出 maxUsers,以证明它实际上没有改变

你会收到三个输入:

  1. 应用名称(字符串)
  2. 版本(字符串)
  3. 最大用户数(数字)

输出应显示配置值、尝试修改时的错误,以及该值保持不变的确认信息:

App: {appName}
Version: {version}
Max Users: {maxUsers}
Error: Config is read-only
Max Users: {maxUsers}

例如,如果输入是 MyApp、2.0 和 100,输出应为:

App: MyApp
Version: 2.0
Max Users: 100
Error: Config is read-only
Max Users: 100

记住:代理模式使用一个供用户操作的空表,而实际数据存放在单独的内部表中。__index 元方法会从数据表中获取值,而 __newindex 会拦截(并阻止)任何写入尝试!

亲手试试

-- 导入 ImmutableConfig 模块
local ImmutableConfig = require('ImmutableConfig')

-- 读取输入
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())

-- TODO:使用 ImmutableConfig.new() 创建配置对象

-- TODO:按以下格式打印全部三个原始值:
-- App: {appName}
-- Version: {version}
-- Max Users: {maxUsers}

-- TODO:尝试修改 maxUsers 字段(这应该会触发错误消息)

-- TODO:再次打印 maxUsers,证明它没有被修改
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Lua 在线编译器