Generika oder Generics wurden in Go ab Version 1.18 eingeführt. Lange Zeit wurde Go als konservative Sprache angesehen, in der Generika zugunsten einer einfacheren Syntax ausgeschlossen wurden, aber mit dem Wachstum der Projekte und der Entwicklung des Ökosystems wuchs der Bedarf an universellen Funktionen und Strukturen. Dies ist besonders wichtig für Containerstrukturen, Algorithmen zur Verarbeitung von Sammlungen und infrastrukturellen Code.
Problem: Vor der Einführung von Generika war es notwendig, Code zu duplizieren oder leere Interfaces (interface{}) zu verwenden, was zu einem Verlust der Typensicherheit und einer geringeren Leistung führte und die Fehlersuche erschwerte.
Lösung: Generika sind in Go durch Typparameter implementiert, die in eckigen Klammern bei Funktionen und Typen angegeben werden. Mit Hilfe von Constraints ist es möglich, die akzeptablen Typparameter einzuschränken. Dies ermöglicht das Schreiben von generischen Funktionen ohne Verlust der Typensicherheit.
Beispielcode:
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) }
Hauptmerkmale:
Kann man arithmetische Operationen (+, -, *, /) für beliebige Typparameter T in Generika verwenden?
Nein. Der Go-Compiler weiß nicht, ob der Typ des Parameters arithmetische Operationen unterstützt. Dazu muss eine Constraint angegeben werden, z. B. ein Interface mit Operatorconstraints ab Go 1.18+.
Beispielcode:
type Addable interface { int | float64 | uint } func Sum[T Addable](slice []T) T { var result T for _, v := range slice { result += v } return result }
Können generische Container Methoden mit unterschiedlichem Verhalten für verschiedene Typen enthalten?
Nein. Die Methoden generischer Strukturen oder Funktionen sind für alle Typen identisch, es sei denn, innerhalb der Methode wird ein Type Switch verwendet. Das Verhalten muss über Constraints definiert oder rein parametrisiert werden.
Kann man Typen mit Typparametern (generische Typen) auf Paketebene erstellen, nicht nur für Funktionen?
Ja, seit Go 1.18 können sowohl generische Funktionen als auch generische strukturelle Typen erstellt werden:
type Stack[T any] struct { items []T } func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
Innerhalb einer Bibliothek zur Arbeit mit Sammlungen wurde eine universelle Map-Funktionalität über interface{} implementiert:
Vorteile:
Nachteile:
Im selben Projekt wechselten wir zu Generika und gaben Einschränkungen über Interfaces an:
Vorteile:
Nachteile: