Метаметоды и классы
Я создал функцию, аналогичную показанной здесь, и у меня возникли проблемы с метаметодом __add. Я хочу иметь возможность использовать метаметод __add в двух экземплярах класса, но единственный способ, которым он работает, - добавить метаметоды в метатаблицу экземпляров. Есть ли способ, которым я мог бы настроить это на работу так, чтобы мой класс или его метатабельность могли иметь метаметод __add и работать при добавлении экземпляров вместе?
function Class(superClass, constructor)
local class = {};
local metatable = {};
if superClass then
metatable.__index = superClass;
end
setmetatable(class, metatable);
function metatable:__call(...)
local instance = {};
setmetatable(instance, { __index = class });
if class.constructor then
class.constructor(instance, ...);
end
return instance;
end
class.constructor = constructor;
return class;
end
Вот пример того, что я хотел бы сделать:
A = Class(nil, function(self, num)
self.num = num;
end);
function A:__add(rhs)
return A(self.num + rhs.num);
end
a = A(1);
b = A(2);
c = a + b;
2 ответа
Благодаря помощи полковника Тридцать два и Итана Рейснера я нашел следующее решение:
function metatable.__call(self, ...)
local instance = {};
setmetatable(instance, self);
if class.constructor then
class.constructor(instance, ...);
end
return instance;
end
Я изменил функцию, чтобы она больше не скрывала переменную self, и использую ее в качестве метатаблицы для экземпляра. Добавление теперь работает как задумано. Тем не менее, это вызвало проблемы в других местах.
Я попробовал несколько вещей, которые нашел в другом месте, и нашел это рабочее решение:
function Class(superClass, constructor)
local class = {};
if superClass then
for k,v in pairs(superClass)
do
class[k] = v;
end
class._superClass = superClass;
end
class.__index = class;
local metatable = {};
function metatable:__call(...)
local instance = {};
setmetatable(instance, class);
if class.constructor then
class.constructor(instance, ...);
end
return instance;
end
class.constructor = constructor;
setmetatable(class, metatable);
return class;
end
На всякий случай это помогает, вот как я обычно создаю свои классы (также легко поддерживает наследование):
function Vector(x, y)
local self = {}
self.x = x
self.y = y
self.magnitude = math.sqrt(x*x + y*y)
function self.unit()
return Vector(x/self.magnitude, y/self.magnitude)
end
function self.printComponents()
print("[" .. self.x .. ", " .. self.y .. "]")
end
--if you want to implement metamethods...
setmetatable(self, {
__add = function(_, other)
return Vector(self.x + other.x, self.y + other.y)
end
})
return self
end
-- now, let's make a ComplexNumber class that inherits from Vector
function ComplexNumber(a, b)
-- define self as an instance of Vector
local self = Vector(a, b)
-- override Vector.printComponents()
function self.printComponents()
print(self.x .. " + " .. self.y .. "i")
end
return self
end
--examples
local v = Vector(10, 20)
v.printComponents() --> [10, 20]
local c = ComplexNumber(10, 5)
c.printComponents() --> 10 + 5i