Fungsi biasa menerima nilai sebagai parameter; fungsi generik melangkah satu tingkat lebih abstrak dengan menerima tipe sebagai parameter. Fitur inilah yang membuat Max pada chapter sebelumnya ditulis sekali tetapi bekerja untuk int dan float64. Chapter ini membahas sintaks deklarasi type parameter pada fungsi, cara compiler mengisi tipe tersebut secara eksplisit maupun lewat inferensi, pemakaian lebih dari satu type parameter, hingga deklarasi type parameter pada tipe seperti struct. Setiap aturan akan kita buktikan lewat satu program utuh di akhir chapter.
Deklarasi Type Parameter #
Type parameter dideklarasikan di dalam kurung siku yang ditulis tepat setelah nama fungsi dan sebelum daftar parameter biasa:
func Identitas[T any](nilai T) T {
return nilai
}Kurung siku [T any] berisi nama type parameter (T) dan constraint-nya (any) yang berarti tipe pengganti boleh tipe apa pun. Setelah dideklarasikan, T dipakai seperti tipe biasa — menjadi tipe parameter nilai sekaligus tipe nilai balik. Nama type parameter mengikuti konvensi singkat: T, U, V untuk tipe umum, K, V untuk pasangan kunci-nilai.
Satu ketentuan penting: setiap type parameter harus bisa ditentukan tipe konkretnya. Jika T tidak pernah muncul pada parameter fungsi, compiler tidak punya petunjuk untuk mengisinya dan pemanggilan seperti F(21) ditolak dengan pesan cannot infer T. Kasus seperti itu tetap sah dengan instansiasi eksplisit.
Instansiasi Eksplisit dan Inferensi Tipe #
Compiler mengisi type parameter lewat dua cara. Instansiasi eksplisit menuliskan tipe konkret di dalam kurung siku saat memanggil: Identitas[int](21) menghasilkan versi Identitas khusus int. Instansiasi implisit membiarkan compiler menyimpulkan tipe dari argumen: Identitas(42) membuat T diisi int secara otomatis — proses ini disebut type inference.
Inferensi bekerja dengan mencocokkan tipe argumen terhadap posisi T pada signature; argumen konstanta tak bertipe memakai default type-nya, misalnya 21 menjadi int. Ketika tipe tidak bisa disimpulkan, instansiasi eksplisit menjadi satu-satunya jalan — sebagaimana pola konstruktor di chapter Generic Type
.
Lebih dari Satu Type Parameter #
Fungsi generik boleh memiliki beberapa type parameter yang dipisah koma, dan setiap type parameter boleh memiliki constraint berbeda:
func Pasangan[K comparable, V any](kunci K, nilai V) string {
return fmt.Sprintf("(%v: %v)", kunci, nilai)
}Type parameter pertama K dibatasi constraint comparable sedangkan V bebas bertipe apa pun. Constraint comparable menandakan tipe pengganti K harus mendukung operator ==, materi yang akan kita bahas tuntas pada chapter Constraint comparable
. Instansiasi eksplisit untuk fungsi seperti ini ditulis berurutan: Pasangan[string, float64]("berat", 62.5).
Type Parameter pada Deklarasi Tipe #
Type parameter tidak milik fungsi saja. Deklarasi tipe — struct, slice bernama, map bernama — juga bisa membawa type parameter:
type Box[T any] struct {
Nama string
Isi T
}Satu deklarasi Box kini menghasilkan keluarga tipe: Box[int], Box[string], dan seterusnya. Field Isi bertipe T sehingga setiap instansiasi menyimpan tipe elemen berbeda, sementara field Nama tetap string untuk semuanya. Pembahasan penuh struct generik beserta method-nya menanti chapter Generic Type
; fokus kita saat ini adalah pola deklarasinya.
Program Demo #
Berkas typeparam-demo.go mempraktikkan seluruh aturan di atas:
package main
import "fmt"
// Identitas menerima satu type parameter T dengan constraint any.
func Identitas[T any](nilai T) T {
return nilai
}
// Lebih dari satu type parameter dipisah koma di dalam kurung siku.
func Pasangan[K comparable, V any](kunci K, nilai V) string {
return fmt.Sprintf("(%v: %v)", kunci, nilai)
}
// Type parameter juga bisa dipakai pada deklarasi tipe.
type Box[T any] struct {
Nama string
Isi T
}
func main() {
// Instansiasi eksplisit: tipe T disebut langsung di dalam kurung siku.
fmt.Println(Identitas[int](21))
// Instansiasi implisit: compiler menyimpulkan T dari argumen.
fmt.Println(Identitas(21 * 2))
// Dua type parameter diisi manual.
fmt.Println(Pasangan[string, float64]("berat", 62.5))
// K=string dan V=string tersimpulkan dari argumen.
fmt.Println(Pasangan("kode", "GO-118"))
// Instansiasi tipe Box dengan dua tipe berbeda.
kotakAngka := Box[int]{Nama: "stok", Isi: 42}
kotakTeks := Box[string]{Nama: "pesan", Isi: "halo"}
fmt.Printf("%s=%d bertipe %T\n", kotakAngka.Nama, kotakAngka.Isi, kotakAngka.Isi)
fmt.Printf("%s=%s bertipe %T\n", kotakTeks.Nama, kotakTeks.Isi, kotakTeks.Isi)
}Output yang Dihasilkan:
21
42
(berat: 62.5)
(kode: GO-118)
stok=42 bertipe int
pesan=halo bertipe stringMari kita telaah baris demi baris program di atas:
- Baris 6 (
func Identitas[T any]): kurung siku ditempatkan setelah nama fungsi;Tbertugas sebagai tipe parameternilaidan tipe nilai balik sekaligus. - Baris 11 (
func Pasangan[K comparable, V any]): dua type parameter dengan constraint berbeda dipisah koma;Kwajib comparable,Vmenerima tipe apa pun. - Baris 16-19 (
type Box[T any] struct): deklarasi tipe generik; fieldIsibertipeTsementaraNamatetapstringdi semua instansiasi. - Baris 23 (
Identitas[int](21)): instansiasi eksplisit —intdituliskan langsung sehingga compiler tidak perlu menebak. - Baris 26 (
Identitas(21 * 2)): instansiasi implisit — ekspresi21 * 2bertipeint, makaTdiisiintoleh type inference. - Baris 29 dan 32 (
Pasangan): baris 29 mengisiKdanVsecara eksplisit; baris 32 membiarkan compiler menyimpulkanK=stringdanV=stringdari kedua argumen. - Baris 35-38 (
kotakAngkadankotakTeks): composite literalBox[int]danBox[string]menghasilkan dua variabel dengan fieldIsibertipe berbeda; verb%Tpadafmt.Printfmembuktikan tipenya masing-masing.
Referensi #
- Spesifikasi Go: Type parameter declarations — aturan resmi deklarasi type parameter.
- Tutorial: Getting started with generics
— tutorial resmi berisi sintaks
[T any]. - Go 1.18 Release Notes — pengenalan type parameter pada Go 1.18.
Type parameter memberi tempat tipe dipasang, tetapi tipe apa saja yang boleh dipasang diatur oleh constraint — komponen yang menentukan batas himpunan tipe sah. Chapter Type Constraint membedah aturan tersebut.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
typeparam-demo.go
Unduh berkaspackage main
import "fmt"
// Identitas menerima satu type parameter T dengan constraint any.
func Identitas[T any](nilai T) T {
return nilai
}
// Lebih dari satu type parameter dipisah koma di dalam kurung siku.
func Pasangan[K comparable, V any](kunci K, nilai V) string {
return fmt.Sprintf("(%v: %v)", kunci, nilai)
}
// Type parameter juga bisa dipakai pada deklarasi tipe.
type Box[T any] struct {
Nama string
Isi T
}
func main() {
// Instansiasi eksplisit: tipe T disebut langsung di dalam kurung siku.
fmt.Println(Identitas[int](21))
// Instansiasi implisit: compiler menyimpulkan T dari argumen.
fmt.Println(Identitas(21 * 2))
// Dua type parameter diisi manual.
fmt.Println(Pasangan[string, float64]("berat", 62.5))
// K=string dan V=string tersimpulkan dari argumen.
fmt.Println(Pasangan("kode", "GO-118"))
// Instansiasi tipe Box dengan dua tipe berbeda.
kotakAngka := Box[int]{Nama: "stok", Isi: 42}
kotakTeks := Box[string]{Nama: "pesan", Isi: "halo"}
fmt.Printf("%s=%d bertipe %T\n", kotakAngka.Nama, kotakAngka.Isi, kotakAngka.Isi)
fmt.Printf("%s=%s bertipe %T\n", kotakTeks.Nama, kotakTeks.Isi, kotakTeks.Isi)
}