0Pricing
Lua Academy · Lección

Introducción a las metatablas

Establezca y obtenga metatablas con setmetatable/getmetatable.

Introducción a las metatablas es una lección gratuita de Lua Academy en CoddyKit. Esta es la lección 1 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.

¿Qué es una metatabla?

Una metatabla es una tabla normal de Lua que controla el comportamiento de otra tabla. Cuando Lua encuentra una operación que no puede realizar sobre una tabla (como una operación aritmética o el acceso a una clave inexistente), busca un metamétodo correspondiente en la metatabla. Este es el mecanismo de extensión de Lua para la sobrecarga de operadores y la POO.

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 y getmetatable

setmetatable(t, mt) establece la metatabla de t en mt y devuelve t. getmetatable(t) devuelve la metatabla, o nil si no existe. Puede proteger una metatabla de modificaciones estableciendo 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 como tabla

Cuando __index es una tabla, Lua busca la clave inexistente en esa tabla. Esta es la base de la herencia mediante prototipos de Lua. Si la clave tampoco está en __index, Lua comprueba su metatabla, y continúa así a lo largo de la cadena.

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 como función

Cuando __index es una función, Lua la llama con la tabla y la clave inexistente. La función puede calcular u obtener el valor dinámicamente. Devuelva nil desde __index para indicar «clave no encontrada».

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 y rawset

rawget(t, k) lee una clave de tabla sin activar __index. rawset(t, k, v) establece una clave sin activar __newindex. Utilícelas para omitir los metamétodos cuando necesite acceder directamente a una tabla desde un metamétodo y evitar una recursión infinita.

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

Varias tablas que comparten una metatabla

Una sola metatabla puede compartirse entre muchas tablas. Así funcionan los sistemas de clases de Lua: todas las instancias de una clase comparten la misma metatabla, que contiene los métodos. Los cambios en la metatabla afectan a todas las instancias.

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

Campo __metatable

Asignar cualquier valor a mt.__metatable hace que getmetatable devuelva ese valor en lugar de la metatabla real. Esto impide que los usuarios inspeccionen o sustituyan la metatabla, lo que resulta útil para la encapsulación y la seguridad.

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

Comprobar si existe una metatabla

Use getmetatable(t) para comprobar si un objeto tiene una metatabla específica (para verificar el tipo). Como __metatable puede ocultar la metatabla real, use rawequal o un campo indicador para realizar comprobaciones fiables de instancia.

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

Metatablas en valores que no son tablas

Las cadenas de Lua ya tienen una metatabla (el módulo string). Por eso puede llamar a s:upper() en cualquier cadena. En Lua puro no puede establecer metatablas en cadenas, números ni booleanos; solo en tablas y 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

Depuración con metatablas

Puede añadir un metamétodo __tostring para controlar cómo tostring() y print() muestran una tabla. Esto resulta muy valioso para depurar objetos complejos.

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)

Comprobación rápida

¿Qué ocurre cuando accede a una clave que no se encuentra en una tabla cuya metatabla tiene __index establecido en otra tabla?

Repaso: metatablas

Resumen:

  • setmetatable(t, mt) / getmetatable(t)
  • __index: tabla → búsqueda en el prototipo; función → despacho dinámico
  • rawget/rawset omiten los metamétodos
  • Las metatablas compartidas permiten un comportamiento similar al de las clases
  • __metatable protege la metatabla frente a la inspección
  • __tostring controla la salida de impresión

Preguntas frecuentes

¿La lección «Introducción a las metatablas» es gratis?

Sí — el texto completo de «Introducción a las metatablas» 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 «Introducción a las metatablas»?

Establezca y obtenga metatablas con setmetatable/getmetatable. 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 1 de 4.

¿Cuánto tiempo toma la lección «Introducción a las metatablas»?

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. Introducción a las metatablas
  2. __index y __newindex
  3. Metamétodos aritméticos
  4. __tostring y __len
← Volver a Lua Academy