Pelajaran modul (3/5)
Generik (Go 1.18+)
Sejak Go 1.18 (Maret 2022), parameter tipe tersedia: fungsi dan tipe dapat diparameterisasi berdasarkan tipe. Mereka memungkinkan Anda menulis Map, Filter, Set[T], LinkedList[T] tanpa menggandakan kode dan tanpa interface{} + pernyataan tipe runtime.
Fungsi umum: sintaksis
func Map[T, U any](s []T, f func(T) U) []U {
out := make([]U, len(s))
for i, v := range s {
out[i] = f(v)
}
return out
}
doubled := Map([]int{1, 2, 3}, func(n int) int { return n * 2 })
// doubled is []int{2, 4, 6}Tiga elemen baru:
[T, U any]setelah nama: mendeklarasikan parameter tipe dengan batasannya.any= batasan yang menerima tipe apa pun (aliasinterface{}sejak Go 1.18).- Inferensi: memanggil
Map([]int{...}, ...)Anda tidak perlu menulisMap[int, int](...): kompiler menyimpulkanT=int, U=int.
Terkadang inferensi saja tidak cukup (misalnya, konstruktor tanpa argumen T) dan diperlukan instantiasi eksplisit: New[*Server]().
Kendala bawaan
| Batasan | Arti |
|---|---|
any | Jenis apa pun (== interface{}). |
comparable | Ketik dengan == dan !=: numerik, string, bool, pointer, saluran, array/struct yang sebanding. |
comparable mengizinkan kesetaraan tetapi tidak < atau >.
func Index[T comparable](s []T, x T) int {
for i, v := range s {
if v == x { return i }
}
return -1
}Untuk </> Anda memerlukan batasan khusus.
Batasan khusus: ketik serikat pekerja
Batasan adalah antarmuka dengan klausa "elemen tipe":
type Ordered interface {
~int | ~int64 | ~float64 | ~string
}
func Min[T Ordered](a, b T) T {
if a < b { return a }
return b
}|= union: menerima salah satu tipe yang terdaftar.~int= "int atau tipe apa pun yang didefinisikan sebagaitype X int" (dengan tipe dasar yang sama). Tanpa~, hanyaintyang tepat.
Batasan juga dapat mencampur metode dan mengetik elemen:
type Stringable interface {
~string
Len() int
}Tipe generik
Tidak hanya fungsi: struct dan antarmuka juga dapat memiliki parameter tipe.
type Set[T comparable] struct {
m map[T]struct{}
}
func NewSet[T comparable]() *Set[T] {
return &Set[T]{m: make(map[T]struct{})}
}
func (s *Set[T]) Add(v T) { s.m[v] = struct{}{} }
func (s *Set[T]) Has(v T) bool { _, ok := s.m[v]; return ok }
s := NewSet[string]()
s.Add("ada")Metode TIDAK BISA menambahkan parameter tipe di luar tipe: func (s *Set[T]) MapTo[U any](...) tidak dapat dikompilasi (batasan desain yang disengaja).
Kapan menggunakan obat generik
Ya ketika:
- Logikanya benar-benar identik untuk beberapa jenis (koleksi, algoritma, pembantu seperti
Map/Filter/Reduce). - Alternatifnya adalah menduplikasi kode atau menggunakan
interface{}+ pernyataan tipe (kehilangan keamanan tipe).
Tidak ketika:
- Antarmuka dengan satu atau dua metode sudah cukup dan lebih mudah dibaca (
io.Reader,fmt.Stringer). - Anda membuat parameter "karena Anda bisa": satu kasus penggunaan → buat versi konkretnya.
Latihan
Definisikan fungsi umum Min[T Dipesan](a, b T) T yang melakukan operasi kecil menggunakan operator <. Il kendala Dipesan è già fornito.
Solusi tersedia setelah 3 upaya
Implementa Map[T, U any](s []T, f func(T) U) []U: menerapkan f elemen ogni dan memulihkan potongan baru.
Solusi tersedia setelah 3 upaya
Kendala apa yang ada di dalamnya menyetujui operator == tetapi NON operator <?
func Index[T ???](s []T, x T) int { ... }