Lompati ke konten utama
Modul 10 · Pelajaran 3 dari 548/50 dalam kursus~15 min
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

Go
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 (alias interface{} sejak Go 1.18).
  • Inferensi: memanggil Map([]int{...}, ...) Anda tidak perlu menulis Map[int, int](...): kompiler menyimpulkan T=int, U=int.

Terkadang inferensi saja tidak cukup (misalnya, konstruktor tanpa argumen T) dan diperlukan instantiasi eksplisit: New[*Server]().

Kendala bawaan

BatasanArti
anyJenis apa pun (== interface{}).
comparableKetik dengan == dan !=: numerik, string, bool, pointer, saluran, array/struct yang sebanding.

comparable mengizinkan kesetaraan tetapi tidak < atau >.

Go
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":

Go
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 sebagai type X int" (dengan tipe dasar yang sama). Tanpa ~, hanya int yang tepat.

Batasan juga dapat mencampur metode dan mengetik elemen:

Go
type Stringable interface {
    ~string
    Len() int
}

Tipe generik

Tidak hanya fungsi: struct dan antarmuka juga dapat memiliki parameter tipe.

Go
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

Latihan#go.m10.l3.e1
Upaya: 0Memuat…

Definisikan fungsi umum Min[T Dipesan](a, b T) T yang melakukan operasi kecil menggunakan operator <. Il kendala Dipesan è già fornito.

Memuat editor…

Solusi tersedia setelah 3 upaya

Latihan#go.m10.l3.e2
Upaya: 0Memuat…

Implementa Map[T, U any](s []T, f func(T) U) []U: menerapkan f elemen ogni dan memulihkan potongan baru.

Memuat editor…

Solusi tersedia setelah 3 upaya

Quiz#go.m10.l3.e3
Siap

Kendala apa yang ada di dalamnya menyetujui operator == tetapi NON operator <?

Go
func Index[T ???](s []T, x T) int { ... }
Pilihan jawaban