What is this tutorial about?
In this tutorial, I will talk about OOP and how you can create classes in Lua.
I’ll start by explaining what OOP is and good use cases for OOP, then move on to actual creating the code.
Before we begin, please read the following:
What is a struct?
A struct is simply a structure.
Everyone has used objects before, right?
An object is a structure, for example.
A struct is simply a template for an object.
What is a class?
A class is a form of structure. It’s a constructor which can create a template object for you. A class can have methods and variables, as well as private methods and variables.
Private methods/variables are things that can only be accessed by the class, but not externally.
Classes are helpful because they make creating objects easier. If we had an object with a lastname
, firstname
and age
property, we could use a class to create a template for all of these properties.
Plan:
- What is OOP?
- What are use cases for OOP?
- How can we implement OOP?
This is a fairly short tutorial, and it shouldn’t take too long for you to read it.
If you find this tutorial useful, let me know so I can create more tutorials.
Part 1: What is OOP?
Object oriented programming is a style of programming. Object oriented programming (or OOP) is something you find most often in high-level languages, like Javascript. We’re focusing on classes in this tutorial. Unfortunately, Lua doesn’t have an easy way of creating classes. This style of programming is geared towards using objects. Hence, ‘object oriented programming’.
Part 2: What are use cases for OOP?
Object oriented programming is very helpful for quite a lot of things. For example, if we had a list of users, we need a sort of template to create it, like C++'s struct
. But… if we need to add functions to that struct
, we need classes. Let’s say that each user looked like this:
{
firstname: 'John',
lastname: 'Doe',
age: 54
}
If we typed that out for each user, it would be inefficient and slow.
Part 3: Implementation
Structs
Structs are incredibly simple to implement.
All we need is a function which returns a newly allocated object.
Like this:
local createUser = function (first, last, age)
local alloc = {}
alloc.firstname = first
alloc.lastname = last
alloc.age = age
return alloc
end
Classes
Classes are little bit more complicated.
We could use the same method as structs, but there’s a much better way.
The reason that the other method isn’t the greatest is because we need a self
property. That method also makes it much harder to create functions for the class.
The self property is a reference to the current object. In Javascript, it’s called this
.
For example, let’s say I had object a
with a method hello
on it. If we call hello
with the :hello() syntax, it will pass in a
as the first argument. Put simply, it is the referenced object.
What is a metatable?
A metatable is much like a proxy.
It ‘intercepts’ requests to an object. For example, if I wanted to ‘intercept’ a ‘get’ to a value, I could add the __index
object/method. It’ll have arguments like name
which we could run operations on and decide a return value. Same goes for set
.
How will we implement this?
- First, we’ll create a base metatable
- Second, we’ll create a ‘new’ function which creates an instance of that base table
- Third, we’ll create our our properties on the new instance
- Last, we’ll return our new table
We can start by creating our base table.
local base = {}
base.__index = base;
What we’re doing is telling our base metatable to reference itself. That way, we can create methods on the base that will work for all instances.
Next, we need to create our ‘new’ function.
function module.new(first, last, age)
end
So far, it’s just a simple function.
Let’s create a new table inside of that function now.
local alloc = {}
alloc.firstname = first
alloc.lastname = last
alloc.age = age
Alright, we have our table. But how can we make it an instance of ‘base’?
Remember how ‘base’ is supposed to be a metatable?
We can use the setmetatable
method which is built-in to Lua.
It takes in an object and a metatable.
Now, let’s add our setmetatable
method.
return setmetatable(alloc, base)
Pretty simple, right?
We’re pretty much done, but at this state, it’s pretty redundant. This is pretty much a struct.
To add a class method, we simply add a method to base
.
Let’s say we wanted to add a getfullname
method. This method will return firstname + ' ' + lastname
.
We need to use the :Function
syntax rather than the .Function
syntax so we get the self
property.
function base:getfullname()
return self.firstname .. ' ' .. self.lastname
end
Now we can test it using our new
method.
In fact, Roblox is smart enough to tell that the result from the new
function has the getfullname
property.
Now, if we do something like this:
local Jack = person.new('Jack', 'Will', 32)
local Mike = person.new('Michael', 'Jackson', 43)
local Alex = person.new('Alexander', 'Williams', 21)
print(Jack:getfullname())
print(Mike:getfullname())
print(Alex:getfullname())
Our output will be:
Jack Will
Michael Jackson
Alexander Williams
Full code:
local person = {}
local base = {}
base.__index = base
function person.new(first, last, age)
local alloc = {}
alloc.firstname = first
alloc.lastname = last
alloc.age = age
return setmetatable(alloc, base)
end
local Jack = person.new('Jack', 'Will', 32)
local Mike = person.new('Michael', 'Jackson', 43)
local Alex = person.new('Alexander', 'Williams', 21)
print(Jack:getfullname())
print(Mike:getfullname())
print(Alex:getfullname())
All of what we just did is equivalent to this Javascript code:
class Person {
constructor(first, last, age) {
this.first = first;
this.last = last;
this.age = age;
}
getfullname() {
return this.first + ' ' + this.last;
}
}
const Jack = new Person('Jack', 'Will', 32);
const Mike = new Person('Michael', 'Jackson', 43);
const Alex = new Person('Alexander', 'Williams', 21);
console.log(Jack.getfullname());
console.log(Mike.getfullname());
console.log(Alex.getfullname());
That’s all!
Now you know how OOP works, congrats! I look forward to seeing what creations you come up with.