0Pricing
Lua Academy · Lekcja

Metody instancji i self

Proszę definiować metody za pomocą składni dwukropka i niejawnego parametru self.

Metody instancji i self to bezpłatna lekcja Lua Academy na CoddyKit. To lekcja 3 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.

self to instancja

Gdy wywołuje Pan/Pani obj:method(), Lua automatycznie przekazuje obj jako pierwszy argument. Zgodnie z konwencją ten parametr nosi nazwę self. Za pośrednictwem self metoda uzyskuje dostęp do danych instancji i wywołuje inne metody.

local Counter = {}
Counter.__index = Counter

function Counter.new(start)
  return setmetatable({value=start or 0, step=1}, Counter)
end

function Counter:increment()
  self.value = self.value + self.step
end

function Counter:getValue() return self.value end

local c = Counter.new(10)
c:increment(); c:increment()
print(c:getValue())  -- 12

Łańcuchowe wywoływanie metod

Należy zwracać self z metod, aby umożliwić łańcuchowe wywoływanie: obj:method1():method2():method3(). Każde wywołanie zwraca ten sam obiekt, umożliwiając płynny styl programowania. Najlepiej sprawdza się to w przypadku metod modyfikujących stan.

local Builder = {}
Builder.__index = Builder

function Builder.new()
  return setmetatable({parts={}},Builder)
end

function Builder:add(s) self.parts[#self.parts+1]=s; return self end
function Builder:addAll(t)
  for _,v in ipairs(t) do self.parts[#self.parts+1]=v end
  return self
end
function Builder:build() return table.concat(self.parts," ") end

local result = Builder.new()
  :add("Hello")
  :add("from")
  :addAll({"Lua","OOP"})
  :build()
print(result)  -- Hello from Lua OOP

Wywoływanie innych metod

Metody mogą wywoływać inne metody tej samej instancji za pomocą self:otherMethod(). Sprzyja to ponownemu wykorzystaniu kodu w obrębie klasy. Prywatne funkcje pomocnicze mogą być zwykłymi funkcjami lokalnymi, które jawnie otrzymują self.

local Stack = {}
Stack.__index = Stack

function Stack.new() return setmetatable({_data={},_size=0},Stack) end
function Stack:push(v) self._size=self._size+1; self._data[self._size]=v end
function Stack:pop()
  if self:isEmpty() then return nil end
  local v=self._data[self._size]; self._data[self._size]=nil
  self._size=self._size-1; return v
end
function Stack:peek()
  return not self:isEmpty() and self._data[self._size] or nil
end
function Stack:isEmpty() return self._size==0 end
function Stack:size() return self._size end

local s=Stack.new(); s:push(1);s:push(2);s:push(3)
print(s:pop(),s:peek(),s:size())  -- 3  2  2

Ostrzeżenie dotyczące przesłaniania metod

Ustawienie pola instancji o takiej samej nazwie jak metoda klasy przesłania tę metodę dla danej instancji. Zwykle jest to błąd — należy zachować ostrożność, gdy nazwy pól instancji mogą kolidować z nazwami metod.

local Dog = {}
Dog.__index = Dog
function Dog.new(n) return setmetatable({name=n},Dog) end
function Dog:speak() print(self.name..": Woof!") end

local d = Dog.new("Rex")
d:speak()        -- Rex: Woof!

-- Accidental shadow:
d.speak = "loud"  -- now d.speak is a string, not a function
local ok,err = pcall(function() d:speak() end)
print(ok, err)   -- false  attempt to call a string value

self w callbackach

Przekazanie metody jako callbacku powoduje utratę niejawnego self. Należy opakować metodę w domknięcie, aby przechwycić self.

local Timer = {}
Timer.__index = Timer
function Timer.new(n) return setmetatable({count=n},Timer) end
function Timer:tick()
  self.count=self.count-1
  print("Ticking, count:", self.count)
end

local t = Timer.new(3)

-- Wrong: loses self
-- local cb = t.tick  -- cb(t) works but cb() doesn't

-- Correct: closure captures self
local cb = function() t:tick() end
cb(); cb(); cb()  -- Ticking 3 times

Metody statyczne a metody instancji

Metodę statyczną wywołuje się na klasie, a nie na instancji. Nie ma ona self (lub otrzymuje klasę jako self). W Lua nie ma formalnego rozróżnienia — zgodnie z konwencją dla metod statycznych należy używać ., a nie :.

local MathUtils = {}
MathUtils.__index = MathUtils

-- Static method (no instance needed)
function MathUtils.clamp(v, lo, hi)
  return math.max(lo, math.min(hi, v))
end

-- Instance method (needs self)
function MathUtils:scale(factor)
  self.value = self.value * factor
end

print(MathUtils.clamp(15, 0, 10))  -- 10
local m = setmetatable({value=5},MathUtils)
m:scale(3)
print(m.value)  -- 15

Metody mixinów

Należy dodać metody z tabeli mixinu do klasy bez formalnego dziedziczenia. Jest to kompozycja płaska: funkcje mixinu są kopiowane do tabeli klasy lub udostępniane przez odwołanie.

local Serializable = {}
function Serializable:serialize()
  local parts={}
  for k,v in pairs(self) do
    if type(v)~="function" then
      parts[#parts+1]=k.."="..tostring(v)
    end
  end
  return "{"..table.concat(parts,",").."}"
end

local User = {}
User.__index = User
User.serialize = Serializable.serialize   -- mixin!

function User.new(n,a) return setmetatable({name=n,age=a},User) end

local u = User.new("Alice",30)
print(u:serialize())

Metody abstrakcyjne

Lua nie wymusza stosowania metod abstrakcyjnych, ale można zdefiniować metodę klasy bazowej, która zgłasza błąd, zmuszając klasy pochodne do jej przesłonięcia. Jest to konwencja oznaczająca „musi zostać przesłonięta”.

local Shape = {}
Shape.__index = Shape

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

function Shape:area()
  error(type(self).." must implement area()",2)
end

function Shape:describe()
  print("Shape area:", self:area())
end

local sq={}
setmetatable(sq,{__index=Shape})
function sq:area() return 4*4 end

sq:describe()  -- Shape area: 16

Tabele metod

W klasach zawierających wiele metod należy organizować je w grupy według funkcjonalności. Wszystkie metody w tej samej tabeli korzystają z tego samego wyszukiwania przez metatabelę, ale można podzielić je według odpowiedzialności za pomocą podprzestrzeni nazw.

local Entity = {}
Entity.__index = Entity

Entity.Physics = {}
function Entity.Physics:applyForce(fx,fy)
  self.vx=(self.vx or 0)+fx
  self.vy=(self.vy or 0)+fy
end

Entity.Render = {}
function Entity.Render:draw()
  print(string.format("Draw at (%g,%g)",self.x or 0,self.y or 0))
end

-- Mix into main class
for k,v in pairs(Entity.Physics) do Entity[k]=v end
for k,v in pairs(Entity.Render) do Entity[k]=v end

function Entity.new(x,y) return setmetatable({x=x,y=y},Entity) end
local e=Entity.new(10,20); e:draw(); e:applyForce(5,0); print(e.vx)

Delegowanie a dziedziczenie

Należy preferować delegowanie (przechowywanie odwołania) zamiast głębokich łańcuchów dziedziczenia. Klasa może delegować zadania innemu obiektowi, zamiast po nim dziedziczyć, dzięki czemu sprzężenie pozostaje niewielkie, a każda klasa zachowuje jasno określoną odpowiedzialność.

local Logger = {}
Logger.__index = Logger
function Logger.new(name) return setmetatable({name=name},Logger) end
function Logger:log(msg) print("["..self.name.."] "..msg) end

local Service = {}
Service.__index = Service

function Service.new(name)
  return setmetatable({
    name=name,
    logger=Logger.new(name)  -- delegation
  },Service)
end

function Service:start()
  self.logger:log("starting...")  -- delegate logging
  print(self.name, "running")
end

Service.new("AuthService"):start()

Szybkie sprawdzenie

Czym różnią się obj:method() i obj.method()?

Podsumowanie: metody instancji

Podsumowanie:

  • Składnia wywołania z dwukropkiem: obj:method() == obj.method(obj)
  • Należy zwracać self, aby umożliwić łańcuchowe wywoływanie metod
  • Inne metody należy wywoływać za pomocą self:otherMethod()
  • Callbacki należy opakowywać w domknięcie, aby zachować self
  • Metody statyczne należy zgodnie z konwencją wywoływać za pomocą notacji kropkowej
  • Dla większej elastyczności należy preferować delegowanie zamiast głębokiego dziedziczenia

Często zadawane pytania

Czy lekcja „Metody instancji i self” jest bezpłatna?

Tak — pełny tekst „Metody instancji i self” 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 „Metody instancji i self”?

Proszę definiować metody za pomocą składni dwukropka i niejawnego parametru self. Ć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 3 z 4.

Ile czasu zajmuje lekcja „Metody instancji i self”?

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. Klasy za pomocą metatabel
  2. Konstruktory i new()
  3. Metody instancji i self
  4. Hermetyzacja za pomocą domknięć
← Powrót do Lua Academy