Generics kini lengkap kita kuasai: type parameter, constraint, type sets, hingga method generik. Alat yang canggih justru menimbulkan godaan baru — memakainya di tempat yang tidak membutuhkannya. Tulisan resmi “When And How To Use Generics” karya Ian Lance Taylor di blog Go dibuka dengan nasihat tegas: jangan memulai kode dengan generics; mulailah dengan kode konkret, lalu perkenalkan type parameter ketika bukti kebutuhannya muncul. Chapter ini merangkum pedoman tersebut, memetakan alternatifnya, lalu menutup dengan tabel keputusan dan program demo yang memakai generics paling ampuh di standard library.

Mulai Tanpa Generics #

Kode konkret — fungsi yang bekerja untuk satu tipe spesifik — adalah titik awal yang benar: paling mudah dibaca, dikompilasi, dan diuji. Generics baru masuk hitungan ketika pola duplikasi dari awal section ini benar-benar muncul: fungsi-fungsi berisi identik yang hanya dibedakan tipenya, atau container yang harus menyimpan tipe elemen apa pun dengan type safety.

Pedoman praktisnya: tulis versi konkretnya lebih dulu, tunggu salinan kedua yang isinya benar-benar sama muncul, baru abstraksikan menjadi fungsi atau tipe generik. Refactor dari konkret ke generik mudah dilakukan kapan pun; sebaliknya, generic yang terlanjur menyebar sulit dirapikan ketika ternyata hanya satu tipe yang dipakai.

Generics Menang pada Container dan Algoritme #

Dua kategori kode mendapat manfaat terbesar. Pertama, container seperti Stack, Queue, dan Set yang perilakunya sama untuk semua tipe elemen. Kedua, algoritme seperti Sort, Index, Filter yang langkah kerjanya identik dan hanya bergantung pada operasi dasar seperti == atau < — persis contoh Stack[T] dan Map/Filter/Reduce dari chapter sebelumnya.

Sinyal lain: Anda menulis any di parameter lalu isinya bercabang type switch hanya untuk mengembalikan tipe aslinya — tanggung jawab tipe yang terdorong ke runtime, yang type parameter kembalikan ke compiler.

Generics di Standard Library #

Standard library Go 1.21 ke atas memuat dua package generik yang menutup banyak kebutuhan sehari-hari, sehingga menulis ulang utilitas sendiri sering tidak perlu:

  • Package slices: Sort, Index, Contains, Equal, Clone untuk slice tipe apa pun.
  • Package maps: Keys, Values, Equal untuk map generik.
  • Package cmp: constraint Ordered plus Compare dan Less.

Mengutamakan stdlib berarti mewarisi pengujian dan optimasi tim Go. Tulis versi sendiri hanya ketika stdlib belum menyediakannya.

Tiga Alternatif yang Bukan Generics #

Tabel berikut memetakan situasi umum ke alat yang tepat:

SituasiAlat yang tepatAlasannya
Pemanggil butuh perilaku berbeda per tipeInterfaceInterface menyatakan kontrak method, bukan himpunan tipe
Hanya satu-dua tipe, logika sederhanaKode konkretPaling mudah dibaca dan diuji
Container/algoritme untuk banyak tipeGenericsSatu implementasi, type safety penuh
Tipe baru diketahui saat runtimeany + reflectionData transit seperti JSON dan template
Utilitas umum sudah ada di stdlibPakai stdlibTeruji dan dioptimasi tim Go

Interface tetap alat utama Go untuk abstraksi perilaku (Read, String, Error). Reflection dipakai encoding/json dan package template untuk tipe yang memang baru diketahui saat runtime — domain yang tidak dijangkau type parameter. any yang tepat hidup di tepi program sebagai data transit, sebagaimana dibahas pada chapter Tipe any .

Program Demo #

Berkas kapan-demo.go mempraktikkan pedoman “pakai stdlib lebih dulu” — seluruhnya memakai fungsi generik resmi dari package slices dan maps:

go
package main

import (
	"fmt"
	"maps"
	"slices"
)

func main() {
	// Semua fungsi package slices generik: satu implementasi
	// bekerja untuk []int, []string, dan tipe lainnya.
	angka := []int{42, 7, 19, 42, 3}

	slices.Sort(angka)
	fmt.Println("Terurut   :", angka)
	fmt.Println("Posisi 19 :", slices.Index(angka, 19))
	fmt.Println("Ada 42    :", slices.Contains(angka, 42))

	teks := []string{"siti", "budi", "dewi"}
	slices.Sort(teks)
	fmt.Println("Terurut   :", teks)
	fmt.Println("Ada budi  :", slices.Contains(teks, "budi"))

	// maps.Keys mengembalikan iterator kunci map;
	// slices.Sorted mengurutkan hasilnya agar output deterministik.
	stok := map[string]int{"jeruk": 8, "apel": 12, "mangga": 15}
	for _, buah := range slices.Sorted(maps.Keys(stok)) {
		fmt.Printf("%-7s: %d\n", buah, stok[buah])
	}
}

Output yang Dihasilkan:

text
Terurut   : [3 7 19 42 42]
Posisi 19 : 2
Ada 42    : true
Terurut   : [budi dewi siti]
Ada budi  : true
apel   : 12
jeruk  : 8
mangga : 15

Mari kita telaah baris demi baris program di atas:

  1. Baris 3-7 (import): tiga package dimuat; slices dan maps adalah package generik resmi sejak Go 1.21, keduanya standard library murni tanpa dependensi eksternal.
  2. Baris 14 (slices.Sort(angka)): memanggil Sort untuk []int — signature aslinya func Sort[S ~[]E, E cmp.Ordered](s S); constraint-nya menyatakan elemen harus terurut, materi chapter Type Constraint .
  3. Baris 16-17: Index memakai constraint comparable sedangkan Contains membungkusnya; keduanya bekerja untuk []int dan []string tanpa satu pun instansiasi eksplisit karena inferensi mengisi tipenya.
  4. Baris 19-22 (teks): barisan fungsi yang sama mengurutkan dan memeriksa slice string — bukti satu implementasi stdlib melayani banyak tipe.
  5. Baris 27 (slices.Sorted(maps.Keys(stok))): maps.Keys mengembalikan iterator kunci map (tipe iter.Seq) sejak Go 1.23; slices.Sorted mengonsumsi iterator dan mengembalikan slice terurut sehingga output tidak terpengaruh acakan urutan map.
  6. Baris 28 (fmt.Printf): verb %-7s meratakan nama buah selebar tujuh karakter; nilai diambil dari stok[buah] — akses per kunci tetap deterministik.

Referensi #

Generics melengkapi interface dan kode konkret dalam kotak alat Go — bukan menggantikannya. Dengan pedoman “mulai konkret, abstraksi saat terbukti perlu”, Anda siap memasuki section berikutnya yang menggabungkan seluruh kontrak data: Concurrency , tempat channel dan goroutine menantang cara kita berpikir tentang alur program.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

kapan-demo.go

Unduh berkas
go
package main

import (
	"fmt"
	"maps"
	"slices"
)

func main() {
	// Semua fungsi package slices generik: satu implementasi
	// bekerja untuk []int, []string, dan tipe lainnya.
	angka := []int{42, 7, 19, 42, 3}

	slices.Sort(angka)
	fmt.Println("Terurut   :", angka)
	fmt.Println("Posisi 19 :", slices.Index(angka, 19))
	fmt.Println("Ada 42    :", slices.Contains(angka, 42))

	teks := []string{"siti", "budi", "dewi"}
	slices.Sort(teks)
	fmt.Println("Terurut   :", teks)
	fmt.Println("Ada budi  :", slices.Contains(teks, "budi"))

	// maps.Keys mengembalikan iterator kunci map;
	// slices.Sorted mengurutkan hasilnya agar output deterministik.
	stok := map[string]int{"jeruk": 8, "apel": 12, "mangga": 15}
	for _, buah := range slices.Sorted(maps.Keys(stok)) {
		fmt.Printf("%-7s: %d\n", buah, stok[buah])
	}
}