4

I'm struggling to understand how metatables work and why they are needed in Lua for creating classes and for inheritance. Every OOP example I find for Lua is a little different from the last, but they always use metatables, specifically for the __index property. Here is how I implemented some simple inheritance:

Animal = {}

function Animal.New()
  local self = {}
  self.sName = "Generic Animal"

  self.GetName = function(self)
    return self.sName
  end

  self.Speak = function(self)
    -- Do nothing, abstract function
  end

  return self
end

Dog = {}

function Dog.New()
  local self = Animal.New()
  self.sName = "Dog"

  self.Speak = function(self)
    print("Bark!")
  end

  return self
end

Labrador = {}

function Labrador.New()
  local self = Dog.New()
  self.sName = "Labrador"
  return self
end

Chihuahua = {}

function Chihuahua.New()
  local self = Dog.New()
  self.sName = "Chihuahua"

  self.Speak = function(self)
    print("Yap yap!")
  end

  return self
end

-- Test --

l = Labrador.New()
print(l:GetName())
l:Speak()

c = Chihuahua.New()
print(c:GetName())
c:Speak()

d = Dog.New()
print(d:GetName())
d:Speak()

a = Animal.New()
print(a:GetName())
a:Speak()

Output:

Labrador
Bark!
Chihuahua
Yap yap!
Dog
Bark!
Generic Animal

So as far as I can see, this works just fine. How would using metatables improve my design?

Joe S
  • 75
  • 1
  • 6
  • 1
    Have you read Chapters 13 and 16 of Programming in Lua, it gives the best explanation I know of. The online version is for 5.0, but I think the basics are still the same. http://www.lua.org/pil/13.html – Jane T Feb 12 '13 at 17:09
  • I hadn't come across that particular chapter (13) in my Google searches. It was actually pretty helpful for understanding metatables better. Thanks. – Joe S Feb 13 '13 at 13:10

1 Answers1

1

Nobody says that metatables are needed for OOP. They're useful, not essential.

Metatables allow you to hide data. I can break all of your careful coding very easily:

local dog = Chihuahua.New()
dog.sName = nil --Oops.
dog.GetName = nil --Oops.

It would also allow other dubious constructs, such as storing other data in the object: dog.newVar = foo.

OOP is about more than just inheritance. Good OOP should also incorporate encapsulation, to maintain the integrity of an object from accidental misuse. Metatables allow you to do this by using an empty table for the main table and filtering all setting and getting through __index and __newindex metamethods. This way, only the object can store data in the actual table. And only the object can retrieve it unless you provide explicit access.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Hmmmmm, I do see what you're saying about encapsulation, but really in Lua, despite someone's best efforts, there isn't much you can do to stop another programmer from messing things up right? Regardless of how you encapsulate things, it can still be just as easy as `Chihuahua = nil`. Also, I haven't had a chance to test it yet, but can't you still mess up function definitions and such despite having them connected to a metatable? In other words, can't `Chihuahua:GetName` still be set to nil regardless of whether it's part of a metatable or not? – Joe S Feb 13 '13 at 13:12
  • @JoeS: Nonsense; Lua has ways to prevent even `Chihuahua = nil` from working. All you have to do is lock down the environment of the script. And Lua has plenty of ways to do that. Yes, if a user is *determined*, then they can break the encapsulation of an object by fetching the metatable (unless you remove the `getmetatable` function. Then they're hosed). But the point is not to prevent malicious use, but *stupidity*. In short, there's no reason you can't do this. – Nicol Bolas Feb 13 '13 at 19:35