Parameter tipe juga berlaku pada deklarasi tipe, sehingga struct dan kontainer dapat ditulis satu kali untuk banyak tipe. Topik ini membahas deklarasi struct generik beserta metodenya, aturan instansiasinya, serta helper, yaitu fungsi bantu siap pakai, pada paket slices dan maps. Paket-paket tersebut menutup kebutuhan pencarian, pengurutan, dan penelusuran peta yang paling sering muncul.
Struct generik#
Deklarasi tipe generik menuliskan parameter tipe pada nama tipenya, misalnya type Kotak[T any] struct. Tipe tersebut baru menjadi konkret setelah argumen tipe diisi, sehingga Kotak[string] dan Kotak[int] merupakan dua tipe berbeda yang berbagi definisi yang sama. Metode menuliskan parameter tipe pada receivernya, dan badan metode memakai T sebagaimana fungsi biasa.
Struct berikut menyimpan satu nilai berapa pun tipenya, dengan field internal yang tidak diekspor sehingga aksesnya diatur melalui metode. Pola ini sangat cocok untuk tipe penampung, karena isi penampung memang ditentukan oleh pemakainya, bukan oleh pembuat tipenya. Selain struct, parameter tipe juga boleh melekat pada deklarasi interface, tetapi struct adalah bentuk yang paling sering dibutuhkan.
package main
import "fmt"
// Kotak menyimpan satu nilai dengan tipe yang ditentukan saat dipakai.
type Kotak[T any] struct {
isi T
}
// Taruh mengganti isi kotak.
func (k *Kotak[T]) Taruh(isi T) {
k.isi = isi
}
// Ambil mengembalikan isi kotak.
func (k Kotak[T]) Ambil() T {
return k.isi
}
func main() {
catatan := Kotak[string]{isi: "kunci lemari"}
fmt.Println(catatan.Ambil())
catatan.Taruh("jurnal suhu")
fmt.Println(catatan.Ambil())
hitungan := Kotak[int]{isi: 24}
fmt.Println(hitungan.Ambil())
}kunci lemari
jurnal suhu
24Literal struct menuliskan argumen tipe secara eksplisit, seperti Kotak[string]{isi: "kunci lemari"}. Nilai nol dari Kotak[string] langsung dapat dipakai karena field isinya berupa string kosong, sedangkan tipe yang memerlukan penyiapan tetap lebih aman melalui fungsi konstruktor. Pemanggilan metode tidak perlu mengulang argumen tipe karena tipenya sudah melekat pada nilai receiver-nya. Berbeda dari pemanggilan fungsi generik, argumen tipe pada literal tidak dapat disimpulkan dari isinya, sehingga Kotak{isi: 24} tanpa argumen tipe ditolak dengan error.
Kontainer generik dengan perilaku kumpulan#
Struct generik umum dipakai untuk kontainer yang menyimpan kumpulan nilai, misalnya tumpukan dengan prinsip terakhir masuk pertama keluar. Field internalnya berupa []T, sehingga setiap instansiasi menyiapkan slice dengan tipe elemen yang seragam. Nilai nol dari tumpukan generik langsung siap dipakai dalam bentuk tumpukan kosong, sehingga deklarasinya tidak memerlukan fungsi konstruktor. Kontainer berbasis any menuntut asersi pada setiap pengambilan nilai dan menerima campuran tipe tanpa peringatan, sedangkan Tumpukan[string] menolak nilai non string saat kompilasi. Tipe elemen yang seragam juga membuat tanda tangan metode jelas, karena Dorong menerima T dan Ambil mengembalikan T.
package main
import "fmt"
// Tumpukan menyimpan nilai dengan prinsip terakhir masuk pertama keluar.
type Tumpukan[T any] struct {
item []T
}
// Dorong menambahkan satu nilai ke puncak tumpukan.
func (t *Tumpukan[T]) Dorong(item T) {
t.item = append(t.item, item)
}
// Ambil mengeluarkan nilai pada puncak tumpukan.
func (t *Tumpukan[T]) Ambil() (T, bool) {
var kosong T
if len(t.item) == 0 {
return kosong, false
}
teratas := t.item[len(t.item)-1]
t.item = t.item[:len(t.item)-1]
return teratas, true
}
// Panjang mengembalikan banyaknya nilai yang tersimpan.
func (t Tumpukan[T]) Panjang() int {
return len(t.item)
}
func main() {
var t Tumpukan[string]
t.Dorong("draf")
t.Dorong("revisi")
t.Dorong("final")
fmt.Println(t.Panjang())
for {
item, ok := t.Ambil()
if !ok {
break
}
fmt.Println(item)
}
_, ok := t.Ambil()
fmt.Println(ok)
}3
final
revisi
draf
falseMetode Ambil mengembalikan dua nilai, yaitu elemen puncak dan penanda keberhasilan, sehingga pemanggilan pada tumpukan kosong tidak memicu panic. Pola dua nilai kembalian ini mengikuti idiom comma-ok yang dipakai pustaka standar. Urutan pengeluarannya kebalikan urutan penyimpanan, dan penanda keberhasilan mencegah pembacaan elemen dari tumpukan yang sudah kosong. Metode Panjang memakai receiver nilai karena hanya membaca panjang slice, sedangkan Dorong dan Ambil memakai receiver pointer karena mengubah isi slice-nya.
Helper paket slices dan maps#
Pustaka standar menyediakan paket slices dan maps yang berisi fungsi generik untuk operasi kumpulan yang paling sering dibutuhkan. Paket slices menyediakan pencarian seperti slices.Index dan slices.Contains, pengurutan slices.Sort, serta pencarian nilai ekstrem slices.Max dan slices.Min. Paket maps menyediakan penelusuran kunci dan nilai peta, termasuk maps.Keys yang sejak Go 1.23 mengembalikan iterator, yaitu nilai fungsi yang menghasilkan kunci satu per satu. Kunci tersebut dikumpulkan dengan slices.Collect terlebih dahulu bila urutannya penting. Sebelum generics tersedia, operasi semacam ini ditulis sendiri atau ditangani lewat paket sort yang berbasis interface. Paket slices menutup kebutuhan itu dengan tanda tangan fungsi yang mempertahankan tipe elemennya.
package main
import (
"fmt"
"maps"
"slices"
)
func main() {
skor := []float64{78.5, 91.0, 66.5, 84.0}
fmt.Println(slices.Max(skor))
fmt.Println(slices.Min(skor))
fmt.Println(slices.Index(skor, 66.5))
fmt.Println(slices.Contains(skor, 100.0))
slices.Sort(skor)
fmt.Println(skor)
jumlahKelas := map[string]int{"A": 24, "B": 18, "C": 21}
kunci := slices.Collect(maps.Keys(jumlahKelas))
slices.Sort(kunci)
for _, k := range kunci {
fmt.Printf("%s: %d\n", k, jumlahKelas[k])
}
}91
66.5
2
false
[66.5 78.5 84 91]
A: 24
B: 18
C: 21Pemanggilan slices.Collect(maps.Keys(jumlahKelas)) mengumpulkan kunci peta menjadi slice, dan slices.Sort mengurutkannya sebelum pencetakan. Langkah pengumpulan dan pengurutan ini perlu karena urutan iterasi peta tidak dijamin, sedangkan keluaran program yang dapat diulang menuntut urutan yang pasti. Fungsi slices.Index mengembalikan posisi pertama atau -1, sedangkan slices.Contains hanya melaporkan keberadaan, dan keduanya menuntut elemen comparable karena pencariannya berbasis kesamaan. Fungsi seperti slices.Max dan slices.Sort menuntut tipe yang mendukung perbandingan urutan, sehingga pemanggilannya pada slice struct biasa ditolak kompilator. Seluruh program pada topik ini dapat dijalankan lewat terminal sebagaimana berikut.
# siapkan folder kerja untuk eksperimen topik ini
mkdir -p /tmp/generics/tipe-generik
cp kotak-catatan.go tumpukan-nilai.go statistik-nilai.go /tmp/generics/tipe-generik/
cd /tmp/generics/tipe-generik
# pastikan tidak ada berkas yang keluar dari format gofmt
gofmt -l .
# jalankan ketiga programnya
go run kotak-catatan.go
go run tumpukan-nilai.go
go run statistik-nilai.gokunci lemari
jurnal suhu
24
3
final
revisi
draf
false
91
66.5
2
false
[66.5 78.5 84 91]
A: 24
B: 18
C: 21Kesalahan umum#
Tiga kesalahan berikut paling sering muncul saat tipe generik baru dipakai. Ketiganya ditolak kompilator atau terlihat pada keluaran program, sehingga perbaikannya dapat diuji langsung.
- Memakai tipe generik tanpa argumen tipe. Deklarasi
var k Kotakditolak dengan pesancannot use generic type Kotak[T any] without instantiation. Perbaikannya, isi argumen tipenya, misalnyavar k Kotak[string]. - Mengira satu struct generik menghasilkan satu tipe tunggal.
Kotak[string]danKotak[int]adalah dua tipe yang berbeda, sehingga satu slice tidak dapat menampung keduanya. Perbaikannya, tetapkan satu argumen tipe untuk setiap penggunaan, atau satukan lewat interface bila yang dikehendaki adalah perilakunya. Penampungan lewat[]anymemang dimungkinkan, tetapi asersinya kembali menumpuk dan manfaat tipe generik hilang. - Mencetak penelusuran peta tanpa mengurutkan kuncinya. Urutan iterasi peta berubah pada setiap eksekusi, sehingga keluarannya tidak dapat dibandingkan antar eksekusi. Perbaikannya, kumpulkan kunci dengan
slices.Collect(maps.Keys(m))lalu urutkan denganslices.Sortsebelum dicetak.
Ringkasan#
- Tipe generik dideklarasikan dengan parameter tipe pada nama tipenya dan menjadi tipe konkret setelah argumen tipe diisi.
- Metode pada tipe generik menuliskan parameter tipe pada receivernya, dan argumen tipe tidak diulang pada pemanggilannya.
- Paket slices dan maps menyediakan fungsi generik siap pakai untuk pencarian, pengurutan, dan penelusuran peta.
maps.Keysmengembalikan iterator sejak Go 1.23, sehingga kunci dikumpulkan denganslices.Collectdan diurutkan bila urutannya penting.
Referensi#
- Package slices — The Go Authors — Fungsi generik pustaka standar untuk slice.
- Package maps — The Go Authors — Fungsi generik pustaka standar untuk peta.
- The Go Programming Language Specification — The Go Project — Aturan resmi deklarasi tipe generik dan instansiasinya.