Programming in Lua - Chapter 21
Object-Oriented Programming
Like everything in Lua, we can use tables as a stand-in for many different structures. We can accomplish Object-Oriented Programming, or a method of programming in which all variables and functions belong to objects, using tables (of course). Objects in OOP need an identity (self), a state, operations, life cycles, etc. Lua can accomplish a lot of this using tables.
While the verbiage used can become interchangeable, we technically define methods as functions that belong to objects. The self as a concept is integral to OOP;
Account = { balance = 0 }
function Account.withdraw (self, v)
self.balance = self.balance - v
end
-- or we can hide self;
function Account:withdraw (v)
self.balance = self.balance - v
-- or, we could have defined the function with the withdraw function;
Account = { balance = 0,
withdraw = function (self, v)
self.balance = self.balance - v
end
}
-- Addtional deposit function
function Account:deposit (v)
self.balance = self.balance + v
end
Classes
So far, we have some OOP attributes, but we need classes. Lua does not ship with any, but metatables can suffice as a good-enough stand in. We can create a prototype object to be used for others upon creation. If we have A and B, and want to make B a prototype of A, we use setmetatable(A, {__index = B}). A will reference B anytime it encounters a function it does not have. To continue on our Bank account example;
local mt = {__index = Account}
function Account.new (o)
o = o or {} -- Create table if user does not provide one
setmetatable(o, mt)
return o
end
a = Account.new{balance = 0}
a:deposit(100.00) -- Many ways to write this, but essentially is equal to Account.deposit(a, 100.00)
All values or fields from Account are carried over into the new creation of a. Even the current code can be improved upon; we can get rid of the extra table made for the metatable and just use Account, and use the colon operator for that nice syntactic sugar;
function Account:new (o)
o = o or {}
self.__index = self
setmetatable(o, self)
return o
end
b = Account:new() -- We used default values in the creation of Account, so that get's inherited giving b.balance an actual value
print(b.balance) --> 0
Inheritance
We’ve seen hints at how inheritance is trivial to implement in Lua. To really show this off, let’s clarify what our Account class looks like now;
Account = {balance = 0}
function Account:new (o)
o = o or {}
self.__index = self
setmetatable(o, self)
return o
end
function Account:deposit (v)
self.balance = self.balance + v
end
function Account:withdraw (v)
if v > self.balance then error"insufficient funds!" end
self.balance = self.balance - v
end
Now, let’s make a new class that inherits all the fields and functions from Account. Then, line 2 is what’s going to actually create a new subclass;
SpecialAccount = Account:new()
s = SpecialAccount:new{limit=1000.00}
Now, s is an object of class SpecialAccount, and anytime we use it the self being used by SpecialAccount is not referring to Account anymore, but SpecialAccount. s’s metatable is SpecialAccount. Now we can create new methods that only belong to SpecialAccount, or even overwrite existing methods;
function SpecialAccount:withdraw (v)
if v - self.balance >= self:getLimit() then
error"Insufficient funds!"
end
self.balance = self.balance - v
end
function SpecialAccount:getLimit ()
return self.limit or 0
end
There are a few more concepts the text reviews (Multiple Inheritance, Privacy, Dual Representation) that’s not super relevant for what I want to use Lua for. So for now, I’ll cut this chapter short.
Exercises
- Implement a class
Stack, with methods topush,pop,top, andisempty. - Implement a class
StackQueueas a subclass ofStack. Add a method forinsertbottom.
Next: Chapter 22