Method pada tipe generik seperti Stack[T] dengan leluasa memakai type parameter milik receivernya — Push(nilai T) adalah contoh yang sudah kita tulis. Pertanyaan yang lebih menarik: bolehkah sebuah method membawa type parameter barunya sendiri, misalnya Ubah[U any] yang mengubah isi Kotak[int] menjadi Kotak[string]? Jawabannya berubah pada Go 1.27: aturan lama melarang total, aturan baru mengizinkan method generik pada tipe konkret tetapi tetap melarangnya pada interface. Chapter ini membedah sejarah aturan tersebut, membuktikan method generik yang kini berfungsi, lalu menguji dua batas yang tetap berlaku dengan pesan compiler asli.
Aturan Lama: Method Harus Tanpa Type Parameter #
Sejak generics diperkenalkan pada Go 1.18 hingga Go 1.26, spesifikasi menetapkan bahwa method tidak boleh mendeklarasikan type parameter sendiri. Kode berikut ditolak selama sembilan tahun rilis:
// Ditolak pada Go 1.18 s.d. 1.26.
func (k Kotak[T]) Ubah[U any](f func(T) U) Kotak[U] {
return Kotak[U]{Isi: f(k.Isi)}
}Compiler era itu membalas dengan pesan method must have no type parameters. Alasannya: method berpotensi memenuhi interface, dan interface yang method-nya punya type parameter menuntut pencocokan setiap kombinasi instansiasi — jauh lebih rumit. Solusi yang menjadi idiom resmi adalah memindahkan fungsi generik ke level paket:
// Solusi klasik: fungsi generik level atas, bukan method.
func Ubah[T, U any](k Kotak[T], f func(T) U) Kotak[U] {
return Kotak[U]{Isi: f(k.Isi)}
}Pemanggilannya berpindah dari k.Ubah(f) menjadi Ubah(k, f) — fungsinya sama, hanya kehilangan tempat di namespace tipe.
Go 1.27: Method Generik Kini Sah #
Rilis Go 1.27 (Agustus 2026) mengubah aturan tersebut: method pada tipe konkret boleh mendeklarasikan type parameter sendiri. Catatan rilisnya menyebut contoh nyata: math/rand/v2 kini mendeklarasikan method generik (*Rand) N[Int intType](n Int) Int yang sebelumnya hanya fungsi generik. Method generik memanggil generic function dalam namespace tipe, jadi k.Ubah(f) kini sah tanpa trik apa pun.
Batasnya tegas dan tetap: interface method tidak boleh memiliki type parameter, dan method interface tidak bisa diimplementasikan oleh method generik. Dua batas inilah yang kita buktikan dengan compiler di bagian berikutnya.
Batas Pertama: Interface Method Tidak Boleh Generik #
Berkas error-interface-method.go mencoba mendeklarasikan interface yang method-nya membawa type parameter:
package main
import "fmt"
type Kotak[T any] struct {
Isi T
}
// Method generik pada tipe konkret sudah sah sejak Go 1.27.
func (k Kotak[T]) Ubah[U any](f func(T) U) Kotak[U] {
return Kotak[U]{Isi: f(k.Isi)}
}
// Interface method tidak boleh memiliki type parameter sendiri.
type Pengubah interface {
Ubah[U any](f func(int) string) Kotak[string]
}
func main() {
var p Pengubah = Kotak[int]{Isi: 21}
fmt.Println(p.Ubah(func(n int) string { return fmt.Sprint(n) }))
}Output yang Dihasilkan:
# command-line-arguments
./error-interface-method.go:16:6: interface method must have no type parameters
./error-interface-method.go:20:19: cannot use Kotak[int]{…} (value of struct type Kotak[int]) as Pengubah value in variable declaration: Kotak[int] does not implement Pengubah (wrong type for method Ubah)
have Ubah[U any](func(int) U) Kotak[U]
want Ubah(func(int) string) Kotak[string]Baris pertama pesan menolak deklarasi Ubah[U any] di dalam interface. Baris-baris berikutnya adalah efek lanjutannya: karena deklarasi interface tidak valid, compiler juga tidak bisa menerima assignment var p Pengubah.
Batas Kedua: Method Generik Tidak Memenuhi Interface #
Kesalahan yang lebih halus terjadi ketika interface dideklarasikan benar — dengan method konkret — tetapi satu-satunya method yang tersedia adalah method generik. Berkas error-implement-interface.go membuktikannya:
package main
import "fmt"
type Kotak[T any] struct {
Isi T
}
// Method generik dengan type parameter sendiri.
func (k Kotak[T]) Ubah[U any](f func(T) U) Kotak[U] {
return Kotak[U]{Isi: f(k.Isi)}
}
// UbahTeks menuntut method konkret, bukan method generik.
type UbahTeks interface {
Ubah(f func(int) string) Kotak[string]
}
func main() {
// Method generik tidak dapat mengimplementasikan method interface.
var u UbahTeks = Kotak[int]{Isi: 21}
fmt.Println(u.Ubah(func(n int) string { return fmt.Sprint(n) }).Isi)
}Output yang Dihasilkan:
# command-line-arguments
./error-implement-interface.go:21:19: cannot use Kotak[int]{…} (value of struct type Kotak[int]) as UbahTeks value in variable declaration: Kotak[int] does not implement UbahTeks (wrong type for method Ubah)
have Ubah[U any](func(int) U) Kotak[U]
want Ubah(func(int) string) Kotak[string]Pesan berbentuk have ... want ... membandingkan dua bentuk method Ubah: yang dimiliki Kotak[int] masih generik ([U any]), sedangkan yang diminta UbahTeks konkret. Method generik tidak bisa “mengerucut” dirinya menjadi satu instansiasi demi memenuhi interface — pencocokan method di interface selalu menuntut kesamaan signature penuh.
Solusi Ketika Interface Tetap Dibutuhkan #
Ketika polimorfisme interface dan transformasi generik sama-sama dibutuhkan, solusinya kembali ke alat lama: fungsi generik level atas. Fungsi Ubah[T, U any](k Kotak[T], f func(T) U) Kotak[U] tetap bekerja pada Go 1.27 dan tidak bertabrakan dengan interface mana pun karena bukan method. Pola ini tetap jawaban utama untuk desain yang berpijak pada interface.
Program Demo #
Berkas methodlimit-demo.go memperagakan method generik Go 1.27 yang berfungsi:
package main
import "fmt"
type Kotak[T any] struct {
Isi T
}
// Method memakai type parameter milik receiver — sah sejak Go 1.18.
func (k Kotak[T]) Cetak(label string) {
fmt.Printf("%s: %v\n", label, k.Isi)
}
// Method generik dengan type parameter sendiri — baru di Go 1.27.
func (k Kotak[T]) Ubah[U any](f func(T) U) Kotak[U] {
return Kotak[U]{Isi: f(k.Isi)}
}
func main() {
asli := Kotak[int]{Isi: 21}
asli.Cetak("asli")
// U tersimpulkan sebagai string dari argumen fungsi.
teks := asli.Ubah(func(n int) string { return fmt.Sprintf("nilai %d", n) })
teks.Cetak("hasil")
ganda := asli.Ubah(func(n int) float64 { return float64(n) * 2 })
ganda.Cetak("ganda")
}Output yang Dihasilkan:
asli: 21
hasil: nilai 21
ganda: 42Mari kita telaah baris demi baris program di atas:
- Baris 10-12 (
Cetak): method biasa pada tipe generik; hanya memakaiTmilik receiver lewat fieldk.Isi, pola yang sudah sah sejak Go 1.18. - Baris 15-17 (
Ubah): method generik dengan type parameter sendiri[U any]; method ini mengubahKotak[T]menjadiKotak[U]dan hanya terkompilasi di Go 1.27 ke atas. - Baris 20 (
Kotak[int]{Isi: 21}): instansiasi eksplisit memastikanT = intuntuk variabelasli. - Baris 24 (
asli.Ubah(func(n int) string ...)): pemanggilan method generik dengan inferensi —Udiisistringdari tipe argumen fungsi, hasilnyaKotak[string]. - Baris 27 (
asli.Ubah(func(n int) float64 ...)): satu method yang sama menghasilkanKotak[float64]karena inferensi mengisiU = float64— inilah kekuatan type parameter milik method. - Baris 21, 25, dan 28 (
Cetak): ketiga nilai dicetak dengan method receiver biasa, membuktikanKotak[int],Kotak[string], danKotak[float64]hidup berdampingan dari satu deklarasi.
Referensi #
- Go 1.27 Release Notes — pengumuman resmi dukungan generic methods beserta batasannya.
- Spesifikasi Go: Method declarations — aturan resmi deklarasi method dan type parameter.
- Tutorial: Getting started with generics — tutorial resmi method pada tipe generik.
Method generik memperkaya kotak alat, tetapi pertanyaan yang lebih penting sekarang: kapan semua alat generics ini layak dipakai? Chapter penutup section ini, Kapan Menggunakan Generics , merangkum pedoman resmi tim Go lengkap dengan tabel keputusan.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
error-implement-interface.go
Unduh berkaspackage main
import "fmt"
type Kotak[T any] struct {
Isi T
}
// Method generik dengan type parameter sendiri.
func (k Kotak[T]) Ubah[U any](f func(T) U) Kotak[U] {
return Kotak[U]{Isi: f(k.Isi)}
}
// UbahTeks menuntut method konkret, bukan method generik.
type UbahTeks interface {
Ubah(f func(int) string) Kotak[string]
}
func main() {
// Method generik tidak dapat mengimplementasikan method interface.
var u UbahTeks = Kotak[int]{Isi: 21}
fmt.Println(u.Ubah(func(n int) string { return fmt.Sprint(n) }).Isi)
}
error-interface-method.go
Unduh berkaspackage main
import "fmt"
type Kotak[T any] struct {
Isi T
}
// Method generik pada tipe konkret sudah sah sejak Go 1.27.
func (k Kotak[T]) Ubah[U any](f func(T) U) Kotak[U] {
return Kotak[U]{Isi: f(k.Isi)}
}
// Interface method tidak boleh memiliki type parameter sendiri.
type Pengubah interface {
Ubah[U any](f func(int) string) Kotak[string]
}
func main() {
var p Pengubah = Kotak[int]{Isi: 21}
fmt.Println(p.Ubah(func(n int) string { return fmt.Sprint(n) }))
}
methodlimit-demo.go
Unduh berkaspackage main
import "fmt"
type Kotak[T any] struct {
Isi T
}
// Method memakai type parameter milik receiver — sah sejak Go 1.18.
func (k Kotak[T]) Cetak(label string) {
fmt.Printf("%s: %v\n", label, k.Isi)
}
// Method generik dengan type parameter sendiri — baru di Go 1.27.
func (k Kotak[T]) Ubah[U any](f func(T) U) Kotak[U] {
return Kotak[U]{Isi: f(k.Isi)}
}
func main() {
asli := Kotak[int]{Isi: 21}
asli.Cetak("asli")
// U tersimpulkan sebagai string dari argumen fungsi.
teks := asli.Ubah(func(n int) string { return fmt.Sprintf("nilai %d", n) })
teks.Cetak("hasil")
ganda := asli.Ubah(func(n int) float64 { return float64(n) * 2 })
ganda.Cetak("ganda")
}