Inheritance with Lua

Now we can discuss how to implement inheritance using Lua’s tables. Let’s start with a sample class with the information from the previous post:

Foo = {}
Foo_mt = { __index = Foo }
function Foo.create()
  local instance = {}
  setmetatable( instance , Foo_mt )
  return instance
end
function Foo.Work()
  print("hi")
end


With this setup, you can create instances of the Foo class by doing the following:

local fooInstance = Foo.create()
fooInstance.Work()

Now what if we define a function on one of these instances?

Bar = Foo.create()
function Bar.Work()
  print("hi bar")
end

At this point, if we call Work() on Bar, it will use the version in the Bar instance, rather than go look at its metatable and find Foo’s version. This is the idea we will use to get inheritance to work in Lua – by defining a new function with the same key as a parent table, you will override it. Now we just have to create a new metatable for Bar, and add a Bar.create() function to generate new tables that use Bar’s metatable:

Bar = Foo.create()
Bar_mt = { __index = Bar}
function Bar.create()
  local instance = {}
  setmetatable( instance , Bar_mt )
  return instance
end
function Bar.Work()
  print("hi bar")
end

This code actually looks a lot like how we declared Foo in the first place, except we are starting with a instance of Foo rather than a blank table. I wrote a helper function to automate all of this.

function CreateClass(base_class)
  local new_class = {}
  if base_class ~= nil then
    new_class = base_class.new()
  end
  local class_mt = { __index = new_class }

  function new_class.new()
    local newinst = {}
    setmetatable( newinst, class_mt )
    return newinst
  end

  return new_class
end

And then you can use it like this:

Foo = CreateClass()
function Foo:Work()
  print("Working foo")
end
function Foo:Work2()
  print("Working foo2")
end

Bar = CreateClass(Foo)
function Bar:Work()
  print("Working bar")
end

local FooInstance = Foo.new()
local BarInstance = Bar.new()

FooInstance:Work()
BarInstance:Work()
FooInstance:Work2()
BarInstance:Work2()

Things do get a little tricker when you want to try to call into your superclass’s function in your override. There is no special keyword you can use, so you have to do it like C++ and call the superclass specifically. Since you don’t want to actually call the function on the main class table (Foo) and you want to call it on your own table instance, you can’t use Foo:Work(). You have to call Foo.Work(self), which calls Foo’s Work() function, but in the context of the current instance.

Bar = CreateClass(Foo)
function Bar:Work()
  Foo.Work(self)
  print("Working bar")
end

Now you know everything I know about Lua. I’ll be learning more as I go, and try to take notes on this blog. Next up I’ll go over some basic helper classes I wrote to manage resources and screens.

Leave a Reply