Generics of generiek zijn geïntroduceerd in Go vanaf versie 1.18. Lange tijd werd Go beschouwd als een conservatieve taal, waarin generics vanwege de eenvoud werden uitgesloten, maar met de groei van het aantal projecten en de ontwikkeling van het ecosysteem ontstond de behoefte aan het schrijven van algemene functies en structuren. Dit is vooral cruciaal voor containerstructuren, algoritmen voor het verwerken van verzamelingen en infrastructuurcode.
Probleem: Tot de komst van generics moest code worden gedupliceerd of waren lege interfaces (interface{}) nodig, wat leidde tot verlies van typeveiligheid en prestaties, evenals tot complicaties bij het debuggen.
Oplossing: Generics zijn in Go geïmplementeerd met behulp van typeparameters, die in vierkante haken bij functies en types worden aangegeven. Met behulp van constraints kunnen de toegestane typeparameters worden beperkt. Dit maakt het mogelijk om algemene functies te schrijven zonder verlies van typeveiligheid.
Voorbeeldcode:
package main import "fmt" type Adder[T any] func(a, b T) T func Sum[T any](slice []T, add Adder[T]) T { var result T for _, v := range slice { result = add(result, v) } return result } func intAdder(a, b int) int { return a + b } func main() { nums := []int{1, 2, 3, 4} sum := Sum(nums, intAdder) fmt.Println(sum) }
Belangrijke kenmerken:
Kun je rekenkundige bewerkingen (+, -, *, /) gebruiken voor elke typeparameter T in generics?
Nee. De Go-compiler weet niet of de parameter type rekenkunde ondersteunt. Om dit te doen, moet je een constraint opgeven, bijvoorbeeld een interface met operator constraints vanaf Go 1.18+.
Voorbeeldcode:
type Addable interface { int | float64 | uint } func Sum[T Addable](slice []T) T { var result T for _, v := range slice { result += v } return result }
Kunnen generic-containers methodes met verschillend gedrag voor verschillende types bevatten?
Nee. Methoden van generic structuren of functies zijn hetzelfde voor alle types, tenzij type switch binnen de methode wordt gebruikt. Het gedrag moet worden gedefinieerd via constraints of puur geparametriseerd zijn.
Kun je types met typeparameters (generieke types) op pakkniveau creëren, en niet alleen voor functies?
Ja, vanaf Go 1.18 kun je zowel generieke functies als generieke struct types maken:
type Stack[T any] struct { items []T } func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
Binnen een bibliotheek voor het werken met verzamelingen werd een universele Map-functionaliteit geïmplementeerd met behulp van interface{}:
Voordelen:
Nadelen:
In hetzelfde project overstapte men op generics en stelde beperkingen in via interfaces:
Voordelen:
Nadelen: