Metode adalah blok kode bernama yang dapat dipanggil berulang kali dari bagian lain program, sehingga logika yang sama tidak perlu ditulis dua kali. Topik ini membahas cara mendefinisikan metode di Crystal beserta fitur parameternya, yaitu nilai default, argumen bernama, nama eksternal, dan overloading. Overloading adalah pendefinisian beberapa metode dengan nama yang sama tetapi tipe parameternya berbeda. Setelah membaca topik ini, sebuah metode dapat didesain dengan antarmuka pemanggilan yang jelas, dan cara compiler memilih salah satu dari beberapa definisi dapat dijelaskan berdasarkan tipe argumennya.
Mendefinisikan metode#
Metode didefinisikan dengan kata kunci def, diikuti nama metode, daftar parameter dalam tanda kurung, lalu tubuh metode yang ditutup dengan end. Nama metode mengikuti konvensi snake case, yaitu huruf kecil yang dipisahkan garis bawah seperti sapa atau hitung_total. Metode mengembalikan nilai dari ekspresi terakhir yang dievaluasi di tubuhnya, sehingga kata kunci pengembalian tidak wajib ditulis.
Program berikut mendefinisikan metode sapa yang menerima satu parameter bernama nama, lalu memanggilnya dua kali.
# Metode sederhana dengan nilai balik dari ekspresi terakhir
def sapa(nama)
"Halo, #{nama}!"
end
p sapa("Ayu")
p sapa("Budi")Jalankan berkasnya dengan perintah berikut.
# jalankan program salam.cr
crystal run salam.cr"Halo, Ayu!"
"Halo, Budi!"Ekspresi terakhir di tubuh sapa adalah sebuah string yang interpolasinya menanamkan nilai parameter nama ke dalam teks. Ekspresi itulah yang dikembalikan kepada pemanggil, dan p menampilkannya pada terminal. Kata kunci return tetap tersedia apabila metode perlu keluar lebih awal dari tengah tubuhnya, tetapi kasus umum tidak memerlukannya.
Nilai default dan argumen bernama#
Parameter dapat diberi nilai default, yaitu nilai yang digunakan apabila pemanggil tidak menyediakan argumennya. Nilai default ditulis setelah tanda sama dengan pada daftar parameter. Di sisi pemanggilan, argumen juga dapat disebut dengan namanya, yang disebut argumen bernama (named arguments), dengan menuliskan nama_parameter: sebelum nilainya. Argumen bernama boleh ditulis dengan urutan apa pun karena compiler mencocokkannya berdasarkan nama, bukan posisi.
Program berikut mempraktikkan ketiga bentuk pemanggilan pada satu metode yang sama.
# Nilai default dan argumen bernama
def sapa(nama, sapaan = "Halo")
"#{sapaan}, #{nama}!"
end
p sapa("Ayu")
p sapa("Ayu", "Selamat pagi")
p sapa(sapaan: "Selamat sore", nama: "Ayu")"Halo, Ayu!"
"Selamat pagi, Ayu!"
"Selamat sore, Ayu!"Pemanggilan pertama menyediakan nama saja, sehingga parameter sapaan menggunakan nilai defaultnya. Pemanggilan kedua menyediakan keduanya secara posisi, sedangkan pemanggilan ketiga menyebut keduanya dengan nama dan urutannya dibalik. Hasil ketiganya tetap tersusun dengan benar karena pencocokan argumen bernama dilakukan berdasarkan nama parameter.
Nama eksternal argumen#
Nama yang digunakan pemanggil dan nama yang digunakan di dalam tubuh metode dapat dibedakan. Penulisan def ukur(panjang _p, lebar _l) menjadikan panjang sebagai nama eksternal, yaitu nama yang digunakan pada situs pemanggilan, sedangkan _p menjadi nama parameter di dalam tubuh metode. Nama eksternal berguna ketika antarmuka pemanggilan ingin dibuat deskriptif tanpa mengorbankan kepraktisan nama internal.
# Nama eksternal: nama di situs pemanggilan dan nama di dalam metode
def ukur(panjang _p, lebar _l)
_p * _l
end
p ukur(panjang: 4, lebar: 3)12Tanda * di depan daftar parameter memperketat aturan itu lagi, yaitu seluruh argumen wajib disebut dengan nama dan pemanggilan secara posisi tidak diterima. Bentuk ini tepat digunakan pada metode yang argumennya mudah tertukar urutannya apabila dipanggil secara posisi.
def ukur(*, panjang _p, lebar _l)
_p * _l
end
p ukur(panjang: 4, lebar: 3)12Kedua versi ukur menghasilkan keluaran yang sama, tetapi versi kedua menutup kemungkinan pemanggilan ukur(4, 3) yang artinya tidak langsung terbaca.
Overloading berbasis tipe#
Nama metode yang sama dapat didefinisikan beberapa kali dengan anotasi tipe yang berbeda pada parameternya. Kuncinya adalah pemilihan dilakukan compiler saat compile dengan mencocokkan tipe argumen pemanggilan terhadap anotasi tipe setiap definisi, bukan saat program berjalan. Dua definisi dengan nama sama dengan demikian merupakan dua metode yang benar-benar berbeda, dan compiler yang memilih salah satunya sebelum program dijalankan.
Program berikut mendefinisikan dua metode hitung dengan tipe parameter yang berbeda, lalu memanggil keduanya.
# Dua metode berbeda dengan nama sama, dipilih compiler
def hitung(x : Int32)
x * 2
end
def hitung(x : String)
x.size
end
p hitung(21)
p hitung("kuda nil")
p typeof(hitung("kuda nil"))42
8
Int32Pemanggilan hitung(21) menyediakan Int32, sehingga definisi pertama yang dipilih dan hasilnya 42. Pemanggilan hitung("kuda nil") menyediakan String, sehingga definisi kedua yang dipilih dan hasilnya panjang string tersebut. Keduanya menghasilkan Int32, tetapi jalur kode yang dijalankannya berbeda karena pemilihan sudah selesai saat compile.
Apabila tidak ada definisi yang cocok dengan tipe argumen, program ditolak compiler sebelum sempat berjalan. Program dengan isi berikut disimpan sebagai salah-overload.cr, lalu dijalankan.
def hitung(x : Int32)
x * 2
end
def hitung(x : String)
x.size
end
p hitung(1.5)Showing last frame. Use --error-trace for full trace.
In salah-overload.cr:9:10
9 | p hitung(1.5)
^
Error: expected argument #1 to 'hitung' to be Int32 or String, not Float64
Overloads are:
- hitung(x : Int32)
- hitung(x : String)Pesan error menyebut baris pemanggilannya, tipe argumen yang tidak dicakup, dan daftar overloading yang tersedia. Perbaikannya ada dua, yaitu menambahkan definisi dengan tipe yang masih kurang atau mengonversi nilai sebelum dipanggil.
Kesalahan umum#
Meletakkan parameter wajib di belakang parameter bernilai default. Compiler menolak definisi tersebut karena pemanggilan secara posisi tidak dapat menentukan nilai parameter wajib yang terhalang nilai default di depannya. Program dengan isi berikut disimpan sebagai salah-default.cr, lalu dijalankan.
def sapa(sapaan = "Halo", nama)
"#{sapaan}, #{nama}!"
end
p sapa("Ayu")In salah-default.cr:1:27
1 | def sapa(sapaan = "Halo", nama)
^
Error: parameter must have a default valuePerbaikannya, tata parameter wajib di depan seperti pada def sapa(nama, sapaan = "Halo"), atau beri nilai default pada semua parameter setelahnya.
Mengira overloading dipilah saat program berjalan. Pemilihan definisi sudah selesai saat compile, sehingga pemanggilan dengan tipe yang tidak dicakup definisi mana pun ditolak dan tidak pernah jatuh ke definisi lain. Overloading bukan pengganti pemeriksaan tipe saat program berjalan, melainkan cara compiler menyusun beberapa versi metode sekaligus di tahap compile.
Ringkasan#
- Metode didefinisikan dengan
defdanend, mengembalikan nilai ekspresi terakhirnya, dan tidak memerlukan kata kunci pengembalian untuk kasus umum. - Parameter dapat memiliki nilai default, dapat disebut dengan nama pada situs pemanggilan, dan dapat diberi nama eksternal yang berbeda dari nama internalnya.
- Tanda
*menjadikan seluruh argumen wajib disebut dengan nama, sehingga pemanggilan secara posisi ditolak. - Overloading mendefinisikan beberapa metode bernama sama dengan tipe parameter berbeda, dan compiler memilih definisinya saat compile berdasarkan tipe argumen pemanggilan.
Referensi#
- Referensi bahasa Crystal — Crystal Language Reference — Dokumentasi sintaks metode, nilai default, argumen bernama, dan overloading.
- Dokumentasi API Crystal 1.22.1 — Dokumentasi API Crystal
— Rincian metode bawaan yang digunakan pada contoh, seperti
p,puts, dantypeof.