0Pricing
Lua Academy · Lección

Métodos de instancia y self

Defina métodos mediante la sintaxis de dos puntos y el parámetro implícito self.

Métodos de instancia y self es una lección gratuita de Lua Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Lua Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Lua Academy incluye 4 lecciones en total.

self es la instancia

Cuando llama a obj:method(), Lua pasa automáticamente obj como primer argumento. Por convención, este parámetro se denomina self. A través de self, el método accede a los datos de la instancia y llama a otros métodos.

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

Encadenamiento de métodos

Devuelva self desde los métodos para permitir el encadenamiento: obj:method1():method2():method3(). Cada llamada devuelve el mismo objeto, lo que permite un estilo fluido. Esto funciona mejor con métodos que modifican el estado.

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

Llamar a otros métodos

Los métodos pueden llamar a otros métodos de la misma instancia mediante self:otherMethod(). Esto fomenta la reutilización del código dentro de una clase. Los auxiliares privados pueden ser funciones locales normales que reciban self explícitamente.

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

Advertencia sobre la sobrescritura de métodos

Establecer un campo de instancia con el mismo nombre que un método de clase oculta el método para esa instancia. Normalmente esto es un error; tenga cuidado cuando los nombres de los campos de instancia puedan entrar en conflicto con los nombres de los métodos.

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 en las callbacks

Pasar un método como callback hace que se pierda el self implícito. Envuelva el método en una clausura para capturar 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

Métodos estáticos frente a métodos de instancia

Un método estático se llama sobre la clase, no sobre una instancia. No tiene self (o recibe la clase como self). En Lua no existe una distinción formal; por convención, use . (no :) para los métodos estáticos.

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

Métodos mixin

Añada métodos de una tabla mixin a una clase sin herencia formal. Esta es una composición plana: copie o referencie las funciones del mixin en la tabla de la clase.

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())

Métodos abstractos

Lua no impone los métodos abstractos, pero puede definir un método de la clase base que lance un error y obligue a las subclases a sobrescribirlo. Esta es una convención para indicar «se debe sobrescribir».

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

Tablas de métodos

En las clases con muchos métodos, organícelos en grupos según su funcionalidad. Todos los métodos de una misma tabla comparten la misma búsqueda mediante metatabla, pero puede dividirlos por responsabilidad usando subespacios de nombres.

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)

Delegación frente a herencia

Prefiera la delegación (mantener una referencia) a las cadenas profundas de herencia. Una clase puede delegar en otro objeto en lugar de heredar de él, lo que mantiene bajo el acoplamiento y permite que cada clase se centre en su función.

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()

Comprobación rápida

¿Cuál es la diferencia entre obj:method() y obj.method()?

Repaso: métodos de instancia

Resumen:

  • Sintaxis de llamada con dos puntos: obj:method() == obj.method(obj)
  • Devuelva self para encadenar métodos
  • Llame a otros métodos: self:otherMethod()
  • Callbacks: envuélvalas en una clausura para conservar self
  • Métodos estáticos: use la notación con punto por convención
  • Prefiera la delegación a la herencia profunda para obtener flexibilidad

Preguntas frecuentes

¿La lección «Métodos de instancia y self» es gratis?

Sí — el texto completo de «Métodos de instancia y self» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Lua Academy, actualiza a CoddyKit PRO. El curso de Lua Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Métodos de instancia y self»?

Defina métodos mediante la sintaxis de dos puntos y el parámetro implícito self. Practicas Lua Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Lua Academy?

No se requiere experiencia previa. Lua Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Métodos de instancia y self»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Lua Academy?

Sí. Cada lección de Lua Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Clases mediante metatablas
  2. Constructores y new()
  3. Métodos de instancia y self
  4. Encapsulación con closures
← Volver a Lua Academy