Metatable adalah table yang dikaitkan pada sebuah nilai untuk mengubah perilaku operasi tertentu, sedangkan metamethod adalah fungsi atau nilai berawalan dua garis bawah dalam metatable yang merespons operasi tersebut. Topik ini membahas setmetatable dan getmetatable, metamethod __index dalam dua bentuknya, pengendalian penulisan lewat __newindex, serta metamethod aritmetika, perbandingan, dan utilitas seperti __tostring. Mekanismenya bersifat umum, dan pola pemrograman berorientasi objek hanyalah salah satu penerapannya.
Mekanisme metatable#
Setiap table dapat membawa satu metatable, dan metatable itulah yang membuat perilaku bawaan table dapat diganti per peristiwa (event), misalnya pencarian kunci, penjumlahan, atau konversi ke string. Fungsi setmetatable mengaitkan metatable pada sebuah table sekaligus mengembalikan table itu, sedangkan getmetatable membacanya kembali. Selama sebuah peristiwa tidak diatur, perilaku bawaan tetap berlaku seperti biasa. Contoh berikut memasang pencarian pengganti (fallback) lewat __index sehingga kunci yang absen jatuh ke table default.
local default = {tema = "terang", ukuran = 14}
local pengaturan = setmetatable({tema = "gelap"}, {__index = default})
print(pengaturan.tema)
print(pengaturan.ukuran)
print(getmetatable(pengaturan).__index == default)gelap
14
trueKunci tema ditemukan langsung pada pengaturan sehingga isi default tidak disentuh, sedangkan ukuran tidak ada dan pencariannya jatuh ke default. Baris ketiga menunjukkan bahwa metatable hanyalah table biasa yang dapat dibaca dan diubah kembali. Nilai string pun sudah membawa metatable bawaan yang menautkan seluruh fungsi pustaka string lewat __index-nya. Selain table, metamethod juga dapat dipasang pada userdata melalui API C, tetapi pada Lua murni table adalah tipe yang paling sering dimanfaatkan. Daftar lengkap peristiwa tercantum pada bagian 2.4 manual, sedangkan topik ini membatasi diri pada peristiwa yang paling sering digunakan.
Metamethod __index#
Bentuk pertama __index adalah table yang menjadi tujuan pencarian pengganti, sebagaimana default pada bagian sebelumnya. Bentuk kedua adalah fungsi yang menerima table dan kunci yang sedang dicari, lalu nilai baliknya digunakan sebagai hasil pencarian. Bentuk fungsi membuka perilaku dinamis, misalnya nilai default yang dihasilkan saat diminta atau lapisan baca-saja yang menyusul pada bagian berikut. Contoh berikut mengisi semua kunci yang belum ada dengan teks penanda.
local profil = setmetatable({}, {
__index = function(t, kunci)
return "(belum diisi)"
end
})
profil.nama = "Dewi"
print(profil.nama)
print(profil.alamat)Dewi
(belum diisi)Fungsi __index hanya dipanggil untuk alamat karena nama sudah tersimpan sebagai kunci sungguhan. Perilaku dinamis semacam ini bekerja untuk kunci apa pun tanpa perlu mendaftarkannya lebih dahulu, sehingga cocok untuk nilai default yang besar atau yang bentuknya baru diketahui saat diminta.
Metamethod __newindex#
Peristiwa __newindex menyadap pemberian nilai pada kunci yang belum ada. Metamethod ini tidak pernah berlaku untuk kunci yang sudah tersimpan, sehingga penyimpanan yang sah harus dilakukan sendiri dengan rawset di dalamnya. Dua kegunaan yang umum adalah validasi penulisan dan table baca-saja berlapis. Contoh pertama menolak nilai negatif untuk stok barang.
local stok = {}
setmetatable(stok, {
__newindex = function(t, kunci, nilai)
if type(nilai) ~= "number" or nilai < 0 then
error("jumlah stok harus berupa bilangan nonnegatif", 0)
end
rawset(t, kunci, nilai)
end
})
stok.pensil = 100
print(stok.pensil)
local ok, pesan = pcall(function() stok.buku = -5 end)
print(ok, pesan)100
false jumlah stok harus berupa bilangan nonnegatifNilai 100 lolos validasi dan disimpan lewat rawset, sedangkan -5 ditolak sehingga pcall menangkap error beserta pesannya. Argumen tingkat 0 pada error menjaga pesan tetap ringkas tanpa tambahan posisi berkas dan baris.
Contoh kedua membungkus table dengan proksi baca-saja. Proksinya kosong, sehingga setiap pembacaan jatuh ke __index menuju table asli dan setiap penulisan pasti tersadap.
local function kunci(t)
local proksi = {}
setmetatable(proksi, {
__index = t,
__newindex = function()
error("table ini hanya untuk dibaca", 0)
end
})
return proksi
end
local resep = {nama = "bakwan", banyak_bahan = 4}
local baca = kunci(resep)
print(baca.nama)
local ok, pesan = pcall(function() baca.nama = "perkedel" end)
print(ok, pesan)bakwan
false table ini hanya untuk dibacaMetamethod aritmetika dan perbandingan#
Peristiwa aritmetika meliputi __add, __sub, __mul, __div, __mod, dan __unm, sedangkan perbandingan menggunakan __eq dan __lt. Metamethod ini menerima operand yang terlibat dan nilai baliknya menjadi hasil operasi, sehingga table dapat berperan layaknya tipe numerik yang lengkap. Kelas Vektor berikut menjumlahkan dan membalik tanda sebuah pasangan koordinat.
local Vektor = {}
Vektor.__index = Vektor
function Vektor.baru(x, y)
return setmetatable({x = x, y = y}, Vektor)
end
function Vektor.__add(a, b)
return Vektor.baru(a.x + b.x, a.y + b.y)
end
function Vektor.__unm(a)
return Vektor.baru(-a.x, -a.y)
end
local p = Vektor.baru(3, 4)
local q = Vektor.baru(1, 2)
print((p + q).x, (p + q).y)
print((-p).x, (-p).y)4 6
-3 -4Ekspresi p + q memanggil __add dengan p dan q sebagai argumen, dan hasilnya berupa Vektor baru sehingga operasi berikutnya dapat dilanjutkan. Perbandingan juga dapat diatur, dan contoh berikut membandingkan pencatatan suhu berdasarkan nilai angkanya.
local Suhu = {}
Suhu.__index = Suhu
function Suhu.baru(nilai)
return setmetatable({nilai = nilai}, Suhu)
end
function Suhu.__eq(a, b)
return a.nilai == b.nilai
end
function Suhu.__lt(a, b)
return a.nilai < b.nilai
end
function Suhu.__tostring(a)
return a.nilai .. " derajat"
end
local pagi = Suhu.baru(24)
local siang = Suhu.baru(28)
local sore = Suhu.baru(24)
print(pagi == sore)
print(pagi == siang)
print(pagi < siang)
print(siang > pagi)
print(tostring(pagi))true
false
true
true
24 derajat__eq dipanggil karena kedua operand adalah table dengan metatable yang sama. Operator > tidak memiliki metamethod sendiri karena a > b dihitung sebagai b < a, sehingga __lt melayani keduanya. Fungsi __tostring mengubah nilai menjadi teks dan berlaku pula saat nilai itu dicetak dengan print, sebagaimana dibahas pada bagian akhir.
__concat, __call, __tostring, dan __len#
Sisa peristiwa yang sering digunakan menutup daftar ini. Peristiwa __concat mengendalikan operator penggabungan .. ketika salah satu operandnya adalah table, __tostring menentukan bentuk teks saat tostring atau print dipanggil, __len mengatur hasil operator #, sedangkan __call membuat table dapat dipanggil layaknya fungsi. Contoh berikut merangkai tiga peristiwa pertama pada table sensor.
local Uraian = {}
Uraian.__index = Uraian
function Uraian.baru(nama)
return setmetatable({nama = nama}, Uraian)
end
function Uraian.__tostring(self)
return "Sensor " .. self.nama
end
function Uraian.__concat(a, b)
return tostring(a) .. tostring(b)
end
function Uraian.__len(self)
return #self.nama
end
local a = Uraian.baru("Ruang A")
local b = Uraian.baru("Ruang B")
print(a .. " dan " .. b)
print(tostring(a))
print(#b)Sensor Ruang A dan Sensor Ruang B
Sensor Ruang A
7Penggabungan pertama memanggil __concat karena operand kirinya table, sedangkan operand bertipe string diproses di dalam metamethod melalui tostring. Nilai balik __len bebas dipilih, sehingga operator # pada b menjawab panjang nama dan bukan jumlah field pada table-nya. Peristiwa __call ditutup dengan penghitung sederhana yang bertambah setiap kali table-nya dipanggil.
local Penghitung = {}
Penghitung.__index = Penghitung
function Penghitung.baru(awal)
return setmetatable({n = awal or 0}, Penghitung)
end
function Penghitung.__call(self, langkah)
self.n = self.n + (langkah or 1)
return self.n
end
function Penghitung.__tostring(self)
return "Penghitung(" .. self.n .. ")"
end
local hitung = Penghitung.baru()
print(hitung())
print(hitung(5))
print(tostring(hitung))1
6
Penghitung(6)Ekspresi hitung() tidak lagi berakhir dengan error karena __call menerima hitung sebagai argumen pertama lalu argumen pemanggilan menyusul setelahnya. Argumen pertama yang berupa table inilah yang pada pola pemrograman berorientasi objek menjadi self.
Ringkasan#
- Metatable mengaitkan metamethod pada nilai sehingga perilaku operasi bawaan dapat diubah tanpa menyentuh isi table.
- __index berbentuk table untuk pencarian pengganti atau fungsi untuk perilaku dinamis, dan hanya aktif pada kunci yang tidak ada.
- __newindex menyadap penulisan kunci baru dan menuntut rawset untuk menyimpan nilai secara langsung.
- Metamethod aritmetika, perbandingan, __concat, __call, __tostring, dan __len memperluas operasi table hingga menyerupai tipe data baru.
Referensi#
- Lua 5.4 Reference Manual — Lua.org — Bagian 2.4 menetapkan daftar peristiwa dan metamethod beserta aturan pemilihannya.
- Programming in Lua, edisi pertama — Lua.org — Bab 13 membahas metamethod aritmetika, relasional, dan akses table termasuk nilai default serta table baca-saja.