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:

go
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:

go
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:

go
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:

go
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:

text
21
42
(berat: 62.5)
(kode: GO-118)
stok=42 bertipe int
pesan=halo bertipe string

Mari kita telaah baris demi baris program di atas:

  1. Baris 6 (func Identitas[T any]): kurung siku ditempatkan setelah nama fungsi; T bertugas sebagai tipe parameter nilai dan tipe nilai balik sekaligus.
  2. Baris 11 (func Pasangan[K comparable, V any]): dua type parameter dengan constraint berbeda dipisah koma; K wajib comparable, V menerima tipe apa pun.
  3. Baris 16-19 (type Box[T any] struct): deklarasi tipe generik; field Isi bertipe T sementara Nama tetap string di semua instansiasi.
  4. Baris 23 (Identitas[int](21)): instansiasi eksplisit — int dituliskan langsung sehingga compiler tidak perlu menebak.
  5. Baris 26 (Identitas(21 * 2)): instansiasi implisit — ekspresi 21 * 2 bertipe int, maka T diisi int oleh type inference.
  6. Baris 29 dan 32 (Pasangan): baris 29 mengisi K dan V secara eksplisit; baris 32 membiarkan compiler menyimpulkan K=string dan V=string dari kedua argumen.
  7. Baris 35-38 (kotakAngka dan kotakTeks): composite literal Box[int] dan Box[string] menghasilkan dua variabel dengan field Isi bertipe berbeda; verb %T pada fmt.Printf membuktikan tipenya masing-masing.

Referensi #

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 berkas
go
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)
}