lqdev
lqdev

Reputation: 403

Lua - trouble understanding OOP

I'm having quite a bit of trouble understanding OOP in Lua. According to Programming in Lua, I can create classes very simply, like this:

Class = {}

function Class:new()
  setmetatable({}, self)
  self.__index = self

  self.a = 1

  return self
end

function Class:incr()
  self.a = 2 * self.a
  return self
end

However, when I go and create an instance, it doesn't work as I would expect:

-- works as expected
instance = Class:new()
instance:incr()
print(instance) --> table 0x(address)
print(instance.a) --> 2

-- it gets weird from here on
other = Class:new()
other:incr():incr()
print(other) --> table 0x(same address)
print(other.a) --> 4

print(instance.a) --> 4

What am I doing wrong?

Upvotes: 1

Views: 126

Answers (2)

wsha
wsha

Reputation: 964

function Class:new()
...
   return self -- self is -> Class = {}
end

other    = Class:new() -- Other will get Class = {} when you call new()
instance = Class:new() -- same here
print(other)    -- will print Class = {}
print(instance) -- will print Class = {}

Upvotes: 1

luther
luther

Reputation: 5544

The example in PiL is frankly confusing. It describes what's called prototypal inheritance. That means that there's no distinction between classes and objects. Here is a version of your new method which is slightly more correct:

function Class:new()
  -- Note that self refers to the class, here. We have to return a new object.
  self.__index = self
  local o = {a=1}
  setmetatable(o, self)
  return o
end

But we still have a problem: Since our constructor sets an instance variable, we can't use it to make subclasses. If we try, those subclasses will all have a set within themselves, instead of in their instances. We could define an init method that we have to call every time we create a new instance, but that would be a pain. Here is one possible way to keep classes and objects separate:

-- Make sure every class has an __index pointing to itself. This lets it be the
-- metatable for both instances and subclasses.
local Class = {}
Class.__index = Class

function Class:new()
  local o = {a=1}
  setmetatable(o, self)
  return o
end

function Class:incr()
  self.a = 2 * self.a
  return self
end

-- To inherit, simply use Class as the metatable.
local Subclass = {}
Subclass.__index = Subclass
setmetatable(Subclass, Class)

This way, new is used only for creating instances.

Upvotes: 1

Related Questions