0Pricing
Lua Academy · Lekcja

Wprowadzenie do metatabel

Proszę ustawiać i pobierać metatabele za pomocą setmetatable/getmetatable.

Wprowadzenie do metatabel to bezpłatna lekcja Lua Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Lua Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Lua Academy zawiera 4 lekcji w sumie.

Czym jest metatabela?

Metatabela to zwykła tabela Lua, która steruje zachowaniem innej tabeli. Gdy Lua napotka operację, której nie może wykonać na tabeli (na przykład działanie arytmetyczne lub odczyt brakującego klucza), wyszukuje odpowiednią metametodę w metatabeli. To mechanizm rozszerzania Lua umożliwiający przeciążanie operatorów i programowanie obiektowe.

local t = {}
local mt = {__index = function(_, k) return "default_" .. k end}

setmetatable(t, mt)

print(t.foo)    -- default_foo
print(t.bar)    -- default_bar
print(t[1])     -- default_1

setmetatable i getmetatable

setmetatable(t, mt) ustawia metatabelę t na mt i zwraca t. getmetatable(t) zwraca metatabelę albo nil, jeśli jej nie ma. Można zabezpieczyć metatabelę przed zmianami, ustawiając mt.__metatable = "protected".

local t = {x = 10}
local mt = {}

setmetatable(t, mt)
print(getmetatable(t) == mt)   -- true

-- Protect metatable
mt.__metatable = "locked"
print(getmetatable(t))   -- locked
-- setmetatable(t, {})  -- ERROR: cannot change protected metatable

__index jako tabela

Gdy __index jest tabelą, Lua wyszukuje brakujący klucz w tej tabeli. Stanowi to podstawę dziedziczenia prototypowego w Lua. Jeśli klucza nie ma również w __index, Lua sprawdza metatabelę tej tabeli i kontynuuje wyszukiwanie w górę łańcucha.

local Animal = {sound = "...", legs = 4}

local Dog = setmetatable({sound = "Woof"}, {__index = Animal})
local Cat = setmetatable({sound = "Meow"}, {__index = Animal})

print(Dog.sound)   -- Woof (own field)
print(Dog.legs)    -- 4 (from Animal)
print(Cat.sound)   -- Meow
print(Cat.legs)    -- 4

__index jako funkcja

Gdy __index jest funkcją, Lua wywołuje ją, przekazując tabelę i brakujący klucz. Funkcja może obliczyć wartość lub pobrać ją dynamicznie. Zwróć nil z __index, aby wskazać, że „nie znaleziono klucza”.

local dynamic = setmetatable({}, {
  __index = function(t, k)
    if k:sub(1,4) == "get_" then
      local field = k:sub(5)
      return function(self) return self[field] end
    end
    return nil
  end
})

dynamic.name = "Lua"
local getter = dynamic.get_name
print(getter(dynamic))   -- Lua

rawget i rawset

rawget(t, k) odczytuje klucz tabeli bez uruchamiania __index. rawset(t, k, v) ustawia klucz bez uruchamiania __newindex. Używaj tych funkcji, aby ominąć metametody, gdy potrzebujesz bezpośredniego dostępu do tabeli wewnątrz metametody i chcesz uniknąć nieskończonej rekurencji.

local logged = setmetatable({}, {
  __newindex = function(t, k, v)
    print("Setting", k, "=", v)
    rawset(t, k, v)  -- bypass __newindex
  end
})

logged.x = 10   -- Setting x = 10
logged.x = 20   -- no log! key now exists in t
print(logged.x) -- 20

Wiele tabel współdzielących metatabelę

Jedna metatabela może być współdzielona przez wiele tabel. Tak działają systemy klas w Lua: wszystkie instancje klasy współdzielą tę samą metatabelę, w której przechowywane są metody. Zmiany w metatabeli wpływają na wszystkie instancje.

local Mt = {
  __index = {
    greet = function(self)
      print("Hello from " .. (self.name or "?"))
    end
  }
}

local a = setmetatable({name="Alice"}, Mt)
local b = setmetatable({name="Bob"},   Mt)

a:greet()   -- Hello from Alice
b:greet()   -- Hello from Bob

Pole __metatable

Ustawienie mt.__metatable na dowolną wartość powoduje, że getmetatable zwraca tę wartość zamiast rzeczywistej metatabeli. Uniemożliwia to użytkownikom sprawdzanie lub zastępowanie metatabeli — jest to przydatne do enkapsulacji i zapewnienia bezpieczeństwa.

local secret = setmetatable({}, {
  __metatable = false,    -- hide metatable
  __index = {value = 42}
})

print(secret.value)        -- 42
print(getmetatable(secret)) -- false (not the real mt)
-- setmetatable(secret, {}) -- ERROR

Sprawdzanie metatabeli

Użyj getmetatable(t), aby sprawdzić, czy obiekt ma określoną metatabelę (na potrzeby sprawdzania typu). Ponieważ __metatable może ukrywać rzeczywistą metatabelę, do niezawodnego sprawdzania w stylu isinstance użyj rawequal lub pola pełniącego funkcję znacznika.

local MyClass = {}
MyClass.__index = MyClass
MyClass.__metatable = MyClass   -- use self as marker

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

local obj = MyClass.new(5)
print(getmetatable(obj) == MyClass)   -- true
print(obj.x)                           -- 5

Metatabele obiektów innych niż tabele

Napisy w Lua mają już metatabelę (moduł string). Dlatego na dowolnym napisie można wywołać s:upper(). W czystym Lua nie można ustawiać metatabel napisom, liczbom ani wartościom logicznym — tylko tabelom i userdata.

local s = "hello"
local mt = getmetatable(s)

-- String metatable has __index = string library
print(mt.__index == string)   -- true

-- So all string functions are methods:
print(s:upper())              -- HELLO
print(s:rep(3, "-"))          -- hello-hello-hello

Debugowanie za pomocą metatabel

Można dodać metametodę __tostring, aby kontrolować sposób wyświetlania tabeli przez tostring() i print(). Jest to nieoceniona pomoc podczas debugowania złożonych obiektów.

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

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

local p = Point.new(3, 4)
print(tostring(p))   -- Point(3, 4)
print(p)             -- Point(3, 4)  (print calls tostring)

Szybkie sprawdzenie

Co się stanie, gdy odczytasz klucz, którego nie ma w tabeli mającej metatabelę z polem __index ustawionym na inną tabelę?

Podsumowanie: metatabele

Podsumowanie:

  • setmetatable(t, mt) / getmetatable(t)
  • __index: tabela → wyszukiwanie w prototypie; funkcja → dynamiczne przekazywanie wywołań
  • rawget/rawset omijają metametody
  • Współdzielone metatabele umożliwiają zachowanie podobne do klas
  • __metatable chroni metatabelę przed sprawdzaniem
  • __tostring kontroluje wynik wyświetlany przez print

Często zadawane pytania

Czy lekcja „Wprowadzenie do metatabel” jest bezpłatna?

Tak — pełny tekst „Wprowadzenie do metatabel” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Lua Academy, przejdź na CoddyKit PRO. Kurs Lua Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Wprowadzenie do metatabel”?

Proszę ustawiać i pobierać metatabele za pomocą setmetatable/getmetatable. Ćwiczysz Lua Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Lua Academy?

Nie wymagamy żadnego doświadczenia. Lua Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „Wprowadzenie do metatabel”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Lua Academy?

Tak. Każda lekcja Lua Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Wprowadzenie do metatabel
  2. __index i __newindex
  3. Metamethody arytmetyczne
  4. __tostring i __len
← Powrót do Lua Academy