Kenapa Generic di Golang Adalah Fitur yang Wajib Dikuasai Setiap Engineer
Dalam dunia software engineering modern, kode yang reusable, aman, dan mudah dirawat bukan lagi sekadar keinginan, tetapi kebutuhan. Salah satu fitur yang mengubah paradigma penulisan kode di Golang adalah Generic. Dengan Generic, engineer bisa menulis kode sekali namun dapat digunakan untuk berbagai tipe data tanpa kehilangan type safety.
Artikel ini akan membahas secara mendalam tentang Generic, termasuk konsep, masalah yang diselesaikan, implementasi di Golang, dan kapan sebaiknya digunakan.
Mengapa Generic Penting
Tanpa Generic, engineer sering terjebak dalam salah satu dari dua masalah:
- Duplikasi Kode: Menulis fungsi atau struktur data yang sama untuk tiap tipe data.
- Interface{} / any: Menggunakan tipe umum yang mengorbankan type safety dan readability.
Generic memecahkan kedua masalah ini dengan:
- Menyediakan type safety compile-time
- Memungkinkan kode reusable dan ekspresif
- Mengurangi duplikasi dan error runtime
Bagi engineer yang mengelola library, API, atau sistem backend besar, memahami Generic adalah keharusan.
Konsep Generic
Generic memungkinkan penggunaan type parameter daripada tipe konkret. Di Golang 1.18+, Generic diperkenalkan dengan sintaks sederhana namun kuat.
Type Parameter
Fungsi generic didefinisikan menggunakan tanda kurung siku []:
func Print[T any](value T) {
fmt.Println(value)
}
Tadalah nama type parameteranyadalah aliasinterface{}
Constraint
Constraint menentukan tipe data yang bisa digunakan:
type Number interface {
~int | ~int64 | ~float64
}
~int→ tipe dasarintdan turunannya|→ union type
Implementasi Generic di Golang
Generic Function
Contoh fungsi Max:
type Ordered interface {
~int | ~int64 | ~float64 | ~string
}
func Max[T Ordered](a, b T) T {
if a > b {
return a
}
return b
}
Penggunaan:
Max(10, 20)
Max("a", "b")
✔ Type-safe, tanpa duplikasi, tanpa casting
Generic Struct
Contoh wrapper response:
type Response[T any] struct {
Data T
Error error
}
Penggunaan:
resp := Response[int]{
Data: 200,
Error: nil,
}
Cocok untuk:
- API response
- Repository pattern
- Service layer
Generic Utility Function
Contoh Map function:
func Map[T any, R any](items []T, fn func(T) R) []R {
result := make([]R, len(items))
for i, v := range items {
result[i] = fn(v)
}
return result
}
Penggunaan:
nums := []int{1, 2, 3}
strings := Map(nums, func(n int) string {
return strconv.Itoa(n)
})
Generic pada Repository Pattern
Untuk backend, generic sangat cocok untuk repository base:
type Repository[T any] interface {
FindByID(id string) (T, error)
Save(entity T) error
}
Implementasi spesifik bisa tetap dilakukan per model, tapi kontrak repository tetap reusable.
Kapan Menggunakan Generic
✅ Ideal
- Logika independen dari tipe data
- Pola berulang (collection, wrapper, utility)
- Menjaga type safety
- Library atau shared module
Contoh: collection utils, DTO wrapper, cache abstraction, repository base
❌ Sebaiknya Dihindari
- Digunakan sekali saja
- Membuat kode lebih sulit dibaca
- Logic sangat spesifik terhadap domain
Generic bukan pengganti design yang buruk, tetapi alat untuk meningkatkan kualitas kode.
Perbandingan Generic vs Interface
| Aspek | Interface | Generic |
|---|---|---|
| Fokus | Perilaku (behavior) | Tipe (type) |
| Polimorfisme | Runtime | Compile-time |
| Type Safety | Lebih lemah | Lebih kuat |
| Overhead | Bisa ada | Minimal |
Rule of thumb: gunakan interface untuk behavior, gunakan generic untuk data & algoritma.
Kesimpulan
Generic adalah fitur kunci di Golang yang wajib dipahami engineer modern. Dengan Generic, kode menjadi:
- Lebih bersih
- Lebih aman
- Lebih reusable
- Lebih ekspresif
Memahami kapan dan bagaimana menggunakan Generic akan membuat Anda menulis kode yang lebih profesional dan siap untuk skala besar.