Go Generics
Tulis fungsi dan tipe yang bekerja untuk banyak tipe.
Pahami konsepnya
Sebelum Go 1.18, fungsi serba-guna memakai interface{} dan kehilangan informasi tipe — atau menulis salinan kode per tipe. Generics mengubah itu: daftar parameter tipe ditulis dalam kurung siku, func Jumlah[T int | float64](angka []T) T, dan T menjadi tipe sungguhan di dalam badan fungsi — pemeriksaan kompilator tetap penuh.
T dibatasi lewat constraint: union tipe seperti int | float64, constraint bernama yang berisi kumpulan tipe berurutan, atau comparable untuk tipe yang mendukung ==. Constraint menentukan operasi apa yang boleh dilakukan pada T.
Selain fungsi, tipe juga generik: type Stack[T any] struct{ items []T } — satu definisi stack untuk semua tipe tanpa assertion. Saat memakainya, kompiler menginstansiasi: Jumlah([]int{...}) menyimpulkan T = int otomatis; Stack[string] ditulis eksplisit untuk tipe.
Coba contoh kode
package main
import "fmt"
func Jumlah[T int | float64](angka []T) T {
var total T
for _, n := range angka {
total += n
}
return total
}
func main() {
fmt.Println(Jumlah([]int{1, 2, 3})) // 6
fmt.Println(Jumlah([]float64{1.5, 2.5})) // 4
}Satu definisi Jumlah melayani dua tipe — total dideklarasikan bertipe T sehingga nilainya nol yang benar per tipe. Pemanggil tak perlu menyebut T: kompiler menyimpulkannya dari argumen. Versi any akan butuh type assertion dan konversi manual di dalam; versi generik tetap type-safe penuh.
Tipe generik: Stack
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
func (s *Stack[T]) Pop() (T, bool) {
var nol T
if len(s.items) == 0 {
return nol, false
}
akhir := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return akhir, true
}
func main() {
s := Stack[string]{}
s.Push("a")
s.Push("b")
v, _ := s.Pop()
fmt.Println(v) // b
}Stack[T] menyimpan tipe apa pun dengan TIPEnya tetap terjaga — Pop mengembalikan (T, bool), bukan (any, bool), sehingga pemakai tak perlu assertion. Receiver juga membawa parameter tipe: (s *Stack[T]). Ini menggantikan pola interface{} lama untuk struktur data generik.
Constraint berbasis interface
type Number interface {
~int | ~int64 | ~float64
}
func RataRata[T Number](angka []T) T {
var total T
for _, n := range angka {
total += n
}
return total / T(len(angka))
}
func main() {
fmt.Println(RataRata([]int{2, 4, 6})) // 4
}Constraint bernama merangkum keluarga tipe — Number mencakup int, int64, float64 beserta tipe turunannya (tanda ~). Fungsi ber constraint ini menerima semuanya tanpa duplikasi, dan badan fungsinya hanya boleh memakai operasi yang dijamin constraint (di sini: + dan /).
Latihan singkat
Uji: buat Map generik untuk len() dan Queue[T] dengan Enqueue/Dequeue — harus `[2 6 2]` lalu `a b`.