I often see that they are used, but I do not understand how to use them myself, what exactly it can be useful for, please give an example of how metatables can be used.
So from what I understand with metatables is (might be inaccurate):
A metatable is a child to another table basically (so the other table is the parent). And basically when something weird happens the metatable consults the Parent and the Parent decides on what is going to happen.
More to learn here:
Yes, I’ve read this topic, but unfortunately I still haven’t found a case to use metatables, everything that is offered there is somehow easier for me to do without using them.
__index metamethod example
local normalTable = {mike = "manhattan", george = "france"}
local metatable = {
__index = function(self,index)
print(index) --hello
print(self==normalTable)--true
return "not found"
end
}
setmetatable(normalTable,metatable)
print(normalTable.hello) --not found
print(normalTable.mike) --manhattan
__call metamethod example
local normalTable = {}
local metatable = {
__call = function(self,index)
print(index) --hello
print(self==normalTable)--true
return "not found"
end
}
setmetatable(normalTable,metatable)
print(normalTable("hello"))--not found
In simple words, you can create your own classes with metatables. For example, you can create a “Gun” class, then use it to easily make guns for your game by just changing properties of the class. If you want to change something for all guns, all you need to do is change the code of “Gun” class.
Metatables are very handy if you want things to be organized, optimized and easy to use.
They are most commonly used for Object Oriented Programming(OOP) in Roblox
If metatables aren’t the thing for you, that is completely fine. DOP and OOP both have their pros and cons.
To answer your question, I’d say that using metatables makes it easier and more convenient to improve code performance. Here’s why :
One the main benefits of using metatables is about Object-Oriented Programming. You may have seen that classes are often defined in this way in Luau/Lua :
local Number = {}
Number.__index = Number
function Number.new(value)
local self = setmetatable({}, Number)
self.value = value
return Number
end
function Number:IncrementValue()
self.value += 1
end
return Number
The reason why we should use metatables is because without using metatables, defining the same class would looks like this :
local Number = {}
function Number.new(value)
local self = {}
self.value = value
function self:IncrementValue()
self.value += 1
end
return self
end
return Number
In these 2 versions, the instances created will be used in the same way:
local number = Number.new(42)
number:IncrementValue()
The problem with the latest version is that each instance of the class will have its own method, which will be identical to those of the other instances of the same class.
This is a problem since the use of a metatable can avoid the need to create a copy of the method for each instance and will instead give a single method shared between all instances of the class thanks to the metamethod __index
.
In this case, we could also say that we can solve this problem without using a metatable by defining the class as follows :
local Number = {}
local Methods = {}
function Number.new(value)
local self = {}
self.value = value
self.Methods = Methods
return self
end
function Methods:IncrementValue()
self.value += 1
end
return Number
But now we have to use the class like this :
local number = Number.new(42)
number.Methods.IncrementValue(number)
So it starts to get complicated to solve the simple problem of duplicating a function unnecessarily. That’s why using a metatable is the perfect solution here.
Metatables are usually used in Object-Oriented Programming (OOP) which you can use to create instances in Lua. Actually, Roblox used this to create instances and datatypes such as Instance.new()
and Vector3.new()
.
Other than that, you can use those to make your tables even more powerful and stronger! Metatables work with the use of metamethods! If you don’t know what metatmethods are, they’re like “events” of a table, such as the PlayerAdded
event of Players
and the Touched
event of BasePart
. If you want to know more about metamethods, check these
https://www.lua.org/manual/5.1/manual.html#2.8
Here are some of the commonly used metamethods:
- __index - This triggers if you’re trying to get something in a table but it’s not there! This can be a function that returns something if someone tries to get an unexisting object from the table OR another table that will be used to get the object if it is not in the table.
local myFruits = {
Apple = "Apple",
Banana = "Banana"
}
print(myFruits.Apple) -- prints "Apple" because it exists inside myFruits
print(myFruits.Banana) -- print "Banana" because it exists inside myFruits
print(myFruits.Orange) -- prints "nil" because it doesn’t exist inside myFruits
local extraFruits = {
Orange = "Orange",
Strawberry = "Strawberry",
Lemon = "Lemon"
}
local metatable = {
__index = extraFruits -- if a fruit is not in myFruits, we'll use extraFruits instead
}
setmetatable(myFruits, metatable) -- setting metatable as the metatable of myFruits
print(myFruits.Apple) --> Apple
print(myFruits.Banana) --> Banana
print(myFruits.Orange) -- now it prints "Orange" instead of "nil"
-
__newindex - This triggers if you’re setting a new value in a table. In other words, if you have a table called “myFruits” and it only contains “Apple” and “Banana”, then you’re trying to add a new fruit called “Orange”, __newindex will run or trigger. This can only be set as a function with the parameters
self
,index
, andvalue
.
local myFruits = {
Apple = "Apple",
Banana = "Banana"
}
local thisIsATable = { -- you can also name your metatable other than "metatable"
__newindex = function(self, index, value)
print(self, index, value) -- this will print the table myFruits and the name of the fruit being added
print("This is not my fruit!")
end
}
setmetatable(myFruits, thisIsATable)
myFruits.Orange = "Orange"
-- doing this will trigger __newidex
-- printing "This is not my fruit!"
-
__add – In my opinion, this is one of the coolest! Using this will make you do anything when you’re trying to add a table to a number! As the name suggests, this will trigger if you’re trying to add the table to a number or another table using + sign. This is a function with the parameters
self
(the table in which the metatable is set to) andvalue
(the number or table you’re trying to add to the table) that returns the result after “adding”.
local myTable = {
Foo = "Foo",
Bar = "Bar"
}
local meta = {
__add = function(self, value)
return 5
end
}
setmetatable(myTable, meta)
print(myTable + 5) -- prints "5" because it is the number returned by __add
print(myTable + 1) --> 5
print(myTable + 9999999999999999) --> 5
print(myTable + 0) --> 5
print(myTable + {}) --> 5
There are many more metamethods out there so feel free to check them out! If you have any questions, feel free to ask me!