0Pricing
Lua Academy · Урок

Одиночное наследование через цепочку __index

Назначайте родительский класс как __index метатаблицы, чтобы унаследовать методы.

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

Цепочка прототипов

Одиночное наследование работает благодаря установке __index дочернего класса в родительский класс. Если метод не найден в дочернем классе, Lua ищет его в родительском. Так создаётся цепочка прототипов.

local Base={}
Base.__index=Base
function Base.new() return setmetatable({},Base) end
function Base:greet() print("Hello from Base") end

local Child={}
Child.__index=Child
setmetatable(Child,{__index=Base})  -- Child inherits Base

function Child.new() return setmetatable({},Child) end

local c=Child.new()
c:greet()  -- Hello from Base (inherited)

Порядок разрешения методов

При вызове метода у экземпляра: (1) поиск в таблице экземпляра, (2) поиск в Child (через __index), (3) поиск в Base (через __index метатаблицы Child). Побеждает первое найденное совпадение.

local Base={}
Base.__index=Base
function Base:who() print("I am Base") end

local Child={}
Child.__index=Child
setmetatable(Child,{__index=Base})
function Child:who() print("I am Child") end  -- override

local b=setmetatable({},Base)
local c=setmetatable({},Child)
b:who()  -- I am Base
c:who()  -- I am Child (overrides)

Наследование конструкторов

Дочерние классы могут повторно использовать конструкторы родителей, вызывая их, или определить собственные конструкторы, расширяющие инициализацию родителя.

local Animal={}
Animal.__index=Animal
function Animal.new(name,sound)
  return setmetatable({name=name,sound=sound},Animal)
end
function Animal:speak() print(self.name..": "..self.sound) end

local Dog={}
Dog.__index=Dog
setmetatable(Dog,{__index=Animal})

function Dog.new(name)
  local self = Animal.new(name,"Woof")
  return setmetatable(self,Dog)
end
function Dog:fetch(item) print(self.name.." fetches "..item) end

local d=Dog.new("Rex")
d:speak(); d:fetch("ball")

Проверка родословной

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

local function instanceof(obj, cls)
  local mt = getmetatable(obj)
  while mt do
    if mt == cls then return true end
    mt = getmetatable(mt)
  end
  return false
end

Вызов родительского метода

Вызовите версию переопределённого метода из родительского класса, обратившись к ней напрямую через таблицу родительского класса.

local Base={}
Base.__index=Base
function Base:init(x) self.x=x end
function Base:value() return self.x end

local Child={}
Child.__index=Child
setmetatable(Child,{__index=Base})
function Child:init(x,y)
  Base.init(self,x)  -- call parent
  self.y=y
end
function Child:value() return Base.value(self)+self.y end

local c=setmetatable({},Child)
c:init(3,4)
print(c:value())  -- 7

Глубокая цепочка

Постройте иерархию из трёх уровней: A → B → C. Поиск метода проходит по всей цепочке, пока метод не будет найден.

local A={}
A.__index=A
function A:aMethod() print("A method") end

local B={}
B.__index=B
setmetatable(B,{__index=A})
function B:bMethod() print("B method") end

local C={}
C.__index=C
setmetatable(C,{__index=B})

local c=setmetatable({},C)
c:bMethod()  -- from B
c:aMethod()  -- from A

Переопределение полей

Экземпляр может переопределить поля уровня класса, установив их напрямую. Поле класса не изменяется — оно лишь скрывается для данного экземпляра.

local Creature={}
Creature.__index=Creature
Creature.hp=100
Creature.speed=10

function Creature.new()
  return setmetatable({},Creature)
end

local fast=Creature.new()
fast.speed=20  -- override for this instance

local normal=Creature.new()

print(fast.speed,normal.speed,Creature.speed)  -- 20  10  10

Наследование на уровне классов

Установка setmetatable(Child, {__index=Parent}) заставляет поиск на уровне класса переходить к родительскому классу. Это означает, что Child.classMethod() найдёт методы родительского класса, если они не переопределены.

local Vehicle={}
Vehicle.__index=Vehicle
Vehicle.maxSpeed=120
function Vehicle.describe() print("Vehicle") end

local Car={}
Car.__index=Car
setmetatable(Car,{__index=Vehicle})

Car.describe()  -- Vehicle (inherited class method)
print(Car.maxSpeed)  -- 120 (inherited class field)

Цепочка инициализации

Для правильной инициализации конструкторы дочерних классов вызывают конструкторы родителей, чтобы состояние родителя было настроено. Выполняйте инициализацию последовательно вверх по иерархии.

local Base={}
Base.__index=Base
function Base:_init(name) self.name=name end

local Mid={}
Mid.__index=Mid
setmetatable(Mid,{__index=Base})
function Mid:_init(name,level)
  Base._init(self,name)
  self.level=level
end

local Top={}
Top.__index=Top
setmetatable(Top,{__index=Mid})
function Top.new(name,level,role)
  local self=setmetatable({},Top)
  Mid._init(self,name,level)
  self.role=role
  return self
end

local t=Top.new("Alice",5,"admin")
print(t.name,t.level,t.role)

Абстрактные базовые классы

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

local Serializer={}
Serializer.__index=Serializer
function Serializer:encode(data)
  error(self._type.." must implement encode()",2)
end
function Serializer:decode(str)
  error(self._type.." must implement decode()",2)
end

local JSONSerializer={_type="JSON"}
setmetatable(JSONSerializer,{__index=Serializer})
function JSONSerializer:encode(data)
  return "{"..tostring(data.key).."}"
end

local j=setmetatable({_type="JSON"},JSONSerializer)
print(j:encode({key="val"}))

Вопрос о наследовании

Что обеспечивает одиночное наследование в Lua?

Итоги: одиночное наследование

Установите setmetatable(Child, {__index = Parent}), чтобы унаследовать методы. Цепочка __index обеспечивает автоматическое делегирование. Переопределяйте методы, определяя их в Child; явно вызывайте родительский метод через Parent.method(self).

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

Урок «Одиночное наследование через цепочку __index» бесплатный?

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

Чему я научусь в уроке «Одиночное наследование через цепочку __index»?

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

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

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

Сколько времени занимает урок «Одиночное наследование через цепочку __index»?

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

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

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

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

  1. Одиночное наследование через цепочку __index
  2. Вызов методов родителя с super
  3. Шаблоны примесей
  4. Переопределение и полиморфизм
← Назад к Lua Academy