Menu
CoddyTech

通过闭包访问

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

如果你无法与私有局部变量交互,它们就毫无用处。解决方法是在构造函数内部定义 getter 和 setter 方法,这样它们就能访问闭包的作用域。

由于这些方法是在 :new() 内创建的,因此它们可以“看到”局部变量。你可以直接将它们赋给对象:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- 私有变量
    
    local obj = {}
    
    -- 在构造函数中定义方法
    function obj:getCount()
        return count
    end
    
    function obj:increment()
        count = count + 1
    end
    
    setmetatable(obj, Counter)
    return obj
end

现在可以访问私有变量 count,但只能通过你提供的方法访问:

local c = Counter:new()
print(c:getCount())  -- 0
c:increment()
c:increment()
print(c:getCount())  -- 2
print(c.count)       -- nil(仍然隐藏!)

注意,:getCount() 和 :increment() 是直接定义在 obj 上的,而不是类表上。每个实例都有自己的这些函数副本,每个副本都可以访问各自私有的 count。这就是权衡:真正的私有性会多占用一些内存,因为方法不会通过原型共享。

challenge icon

挑战

简单

让我们创建一个使用基于闭包的隐私机制来存储摄氏温度值的 Temperature 类,并在构造函数中定义 getter 和 setter 方法,以便受控地访问温度值!

你会把代码分成两个文件:

  • Temperature.lua:创建一个类,在构造函数内部用局部变量存储温度值,使其完全无法从外部访问。构造函数 :new(initialCelsius) 应将温度存储在局部变量中,然后直接在对象实例上定义以下方法:
    • :getCelsius():返回当前的摄氏温度
    • :setCelsius(value):将温度更新为新值
    • :getFahrenheit():使用公式 (celsius * 9/5) + 32 将温度转换为华氏温度并返回
  • main.lua:引入 Temperature 模块,并从输入中读取两个值:初始温度和要设置的新温度。使用初始值创建一个 Temperature 实例,打印摄氏温度和华氏温度,然后使用 setter 更改温度,并再次打印这两个温度读数。

你会收到两个输入:

  1. 初始摄氏温度(一个数字)
  2. 要设置的新温度(一个数字)

输出应为四行,显示温度更改前后的读数:

Celsius: {initialValue}
Fahrenheit: {convertedInitial}
Celsius: {newValue}
Fahrenheit: {convertedNew}

例如,如果输入是 0 和 100,输出应为:

Celsius: 0
Fahrenheit: 32
Celsius: 100
Fahrenheit: 212

请记住:温度值应存储在 :new() 内部的局部变量中,而不是存储在 self 上。getter 和 setter 方法定义在构造函数内部,因此可以通过闭包访问这个私有变量。直接访问 temp.celsius 或 temp._celsius 应返回 nil!

亲手试试

-- 加载 Temperature 模块
local Temperature = require('Temperature')

-- 读取输入值
local initialTemp = tonumber(io.read())
local newTemp = tonumber(io.read())

-- TODO:用初始值创建一个 Temperature 实例

-- TODO:打印初始的摄氏度和华氏度读数

-- TODO:使用设置方法将温度改为 newTemp

-- TODO:打印更新后的摄氏度和华氏度读数
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: Lua 在线编译器