0Pricing
Lua Academy · Урок

Классы с помощью метатаблиц

Моделируйте классы с помощью таблиц и __index для поиска методов.

«Классы с помощью метатаблиц» — бесплатный урок Lua Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Lua Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Lua Academy содержит 4 уроков всего.

Шаблон класса на основе таблицы

В Lua «класс» — это всего лишь таблица, хранящая методы. Экземпляры являются таблицами, у метатаблицы которых __index указывает на таблицу класса. Благодаря этому все методы доступны экземплярам через цепочку поиска.

local Animal = {}
Animal.__index = Animal

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

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

local dog = Animal.new("Rex","Woof")
dog:speak()  -- Rex says Woof

__index указывает на класс

Установка ClassName.__index = ClassName означает «искать отсутствующие ключи в таблице класса». При вызове метода экземпляра Lua не находит его в экземпляре, проверяет метатаблицу (__index), находит класс и ищет метод уже в нём.

local Point = {}
Point.__index = Point

Point.type = "2D Point"

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

function Point:magnitude()
  return math.sqrt(self.x^2 + self.y^2)
end

local p = Point.new(3,4)
print(p:magnitude())   -- 5.0
print(p.type)          -- 2D Point (from class)

Шаблон конструктора

Функция new() — это конструктор: она создаёт таблицу с данными экземпляра и возвращает её, обернув в метатаблицу, указывающую на класс. Это стандартный способ создания объектов в Lua.

local Circle = {}
Circle.__index = Circle

function Circle.new(cx, cy, radius)
  assert(radius > 0, "radius must be positive")
  return setmetatable({cx=cx, cy=cy, r=radius}, Circle)
end

function Circle:area()
  return math.pi * self.r^2
end

function Circle:perimeter()
  return 2 * math.pi * self.r
end

local c = Circle.new(0, 0, 5)
print(string.format("Area: %.2f", c:area()))

Синтаксис метода и функции

obj:method() — это сокращённая запись для obj.method(obj). Определяйте методы с синтаксисом двоеточия (function Cls:method()), чтобы неявно получить self. Вызывайте их с двоеточием, чтобы передать экземпляр. Смешивание точечного синтаксиса и синтаксиса с двоеточием приводит к трудноуловимым ошибкам.

local Dog = {}
Dog.__index = Dog

-- Define with colon: self is implicit
function Dog:bark(times)
  for i = 1, (times or 1) do
    print(self.name .. ": Woof!")
  end
end

function Dog.new(name)
  return setmetatable({name=name}, Dog)
end

local d = Dog.new("Buddy")
d:bark(2)        -- Buddy: Woof! (x2)
-- Dog.bark(d,1) -- same thing, explicit self

Поля класса и поля экземпляра

Поля в таблице класса являются общими для всех экземпляров (переменные класса). Поля в таблице экземпляра относятся только к этому экземпляру. Экземпляр может скрыть поле класса, создав собственное поле с таким же именем.

local Entity = {}
Entity.__index = Entity
Entity.count = 0    -- class-level counter

function Entity.new(name)
  Entity.count = Entity.count + 1
  return setmetatable({name=name, id=Entity.count}, Entity)
end

local a = Entity.new("alpha")
local b = Entity.new("beta")
local c = Entity.new("gamma")

print(Entity.count)  -- 3
print(a.id, b.id, c.id)  -- 1  2  3

tostring для классов

Добавьте __tostring в таблицу класса, чтобы вывод при печати был понятнее. Поскольку метатаблицей экземпляра является класс, а в классе есть __tostring, все экземпляры будут использовать его автоматически.

local Vec2 = {}
Vec2.__index = Vec2
Vec2.__tostring = function(v)
  return string.format("Vec2(%g, %g)", v.x, v.y)
end

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

function Vec2.__add(a, b)
  return Vec2.new(a.x+b.x, a.y+b.y)
end

local v1 = Vec2.new(1,2)
local v2 = Vec2.new(3,4)
print(v1+v2)   -- Vec2(4, 6)

Проверка типа экземпляра

Проверьте, является ли объект экземпляром класса, сравнив его метатаблицу с таблицей класса. Это эквивалент операции instanceof в Lua.

local Dog = {}
Dog.__index = Dog

function Dog.new(name)
  return setmetatable({name=name}, Dog)
end

local function isDog(obj)
  return getmetatable(obj) == Dog
end

local d = Dog.new("Rex")
local t = {}
print(isDog(d))  -- true
print(isDog(t))  -- false

Приватные методы

Приватные методы — это локальные функции в файле модуля. Их нельзя вызвать извне, но они доступны внутри общедоступных методов модуля как внешние переменные.

local Account = {}
Account.__index = Account

-- Private helper
local function validate(amount)
  return type(amount)=="number" and amount > 0
end

function Account.new(balance)
  return setmetatable({balance=balance or 0}, Account)
end

function Account:deposit(amount)
  assert(validate(amount), "invalid amount")
  self.balance = self.balance + amount
end

function Account:getBalance() return self.balance end

Сравнение классов

Переопределите __eq и __lt для пользовательского сравнения на равенство и упорядочивания. Lua вызывает __eq для таблиц только тогда, когда оба операнда используют одну и ту же метатаблицу.

local Temp = {}
Temp.__index = Temp
Temp.__eq = function(a,b) return a.celsius == b.celsius end
Temp.__lt = function(a,b) return a.celsius < b.celsius end
Temp.__tostring = function(t)
  return t.celsius.."°C"
end

function Temp.new(c)
  return setmetatable({celsius=c}, Temp)
end

local t1 = Temp.new(100)
local t2 = Temp.new(0)
print(t1 == Temp.new(100))  -- true
print(t2 < t1)              -- true

Несколько классов

Каждый класс — независимая таблица со своей метатаблицей. Объекты разных классов имеют разные метатаблицы. Это основа объектно-ориентированной системы с несколькими классами в Lua.

local Cat = {}
Cat.__index = Cat

function Cat.new(name) return setmetatable({name=name,lives=9},Cat) end
function Cat:loseLife() self.lives=self.lives-1; return self.lives end

local Dog2 = {}
Dog2.__index = Dog2
function Dog2.new(name) return setmetatable({name=name,tricks={}},Dog2) end
function Dog2:learn(t) self.tricks[#self.tricks+1]=t end

local c = Cat.new("Whiskers")
local d = Dog2.new("Buddy")
d:learn("sit"); d:learn("stay")
print(c:loseLife(), #d.tricks)  -- 8  2

Быстрая проверка

Зачем устанавливать ClassName.__index = ClassName?

Итоги: классы через метатаблицы

Краткое содержание:

  • Класс = таблица + __index = self
  • Конструктор: function Cls.new(...) return setmetatable({}, Cls) end
  • Методы: определяются в таблице класса и вызываются с синтаксисом двоеточия
  • Поля класса и экземпляра: поля класса являются общими, поля экземпляра относятся к отдельному объекту
  • Проверка типа: getmetatable(obj) == Cls

Часто задаваемые вопросы

Урок «Классы с помощью метатаблиц» бесплатный?

Да — полный текст урока «Классы с помощью метатаблиц» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Lua Academy, подпишись на CoddyKit PRO. Курс Lua Academy содержит 4 уроков всего.

Чему я научусь в уроке «Классы с помощью метатаблиц»?

Моделируйте классы с помощью таблиц и __index для поиска методов. Ты практикуешь Lua Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Lua Academy?

Предыдущий опыт не требуется. Lua Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Классы с помощью метатаблиц»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Lua Academy?

Да. Каждый урок Lua Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Классы с помощью метатаблиц
  2. Конструкторы и new()
  3. Методы экземпляров и self
  4. Инкапсуляция с замыканиями
← Назад к Lua Academy