Direkt zum Hauptinhalt springen
eLearner.app
Modul 10 · Lektion 3 von 548/50 im Kurs~15 min
Lektionen des Moduls (3/5)

Generics (Go 1.18+)

Seit Go 1.18 (März 2022) sind Typparameter verfügbar: Funktionen und Typen können über Typen parametrisiert werden. Mit ihnen können Sie Map, Filter, Set[T], LinkedList[T] schreiben, ohne Code zu duplizieren und ohne interface{} + Laufzeittypzusicherungen.

Generische Funktion: die Syntax

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}

Drei neue Elemente:

  • [T, U any] nach dem Namen: deklariert die Typparameter mit ihren Einschränkungen.
  • any = Einschränkung, die jeden Typ akzeptiert (Alias ​​von interface{} seit Go 1.18).
  • Inferenz: Beim Aufruf von Map([]int{...}, ...) müssen Sie nicht Map[int, int](...) schreiben: Der Compiler leitet T=int, U=int ab.

Manchmal reicht die Inferenz nicht aus (z. B. Konstruktoren ohne T-Argumente) und eine explizite Instanziierung ist erforderlich: New[*Server]().

Eingebaute Einschränkungen

EinschränkungBedeutung
anyBeliebiger Typ (== interface{}).
comparableTypen mit == und !=: numerisch, String, Bool, Zeiger, Kanäle, vergleichbare Arrays/Strukturen.

comparable erlaubt Gleichheit, aber nicht < oder >.

Go
func Index[T comparable](s []T, x T) int {
    for i, v := range s {
        if v == x { return i }
    }
    return -1
}

Für </> benötigen Sie eine benutzerdefinierte Einschränkung.

Benutzerdefinierte Einschränkung: Typvereinigungen

Eine Einschränkung ist eine Schnittstelle mit „Typelement“-Klauseln:

Go
type Ordered interface {
    ~int | ~int64 | ~float64 | ~string
}

func Min[T Ordered](a, b T) T {
    if a < b { return a }
    return b
}
  • | = Union: akzeptiert jeden der aufgelisteten Typen.
  • ~int = „int oder jeder Typ, der als type X int definiert ist“ (mit demselben zugrunde liegenden Typ). Ohne ~, nur exakter int.

Eine Einschränkung kann auch Methoden und Typelemente mischen:

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

Generische Typen

Nicht nur Funktionen: Strukturen und Schnittstellen können auch Typparameter haben.

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")

Methoden KÖNNEN KEINE Typparameter hinzufügen, die über die des Typs hinausgehen: func (s *Set[T]) MapTo[U any](...) wird nicht kompiliert (absichtliche Designeinschränkung).

Wann sollten Generika verwendet werden?

Ja wenn:

  • Die Logik ist für mehrere Typen (Sammlungen, Algorithmen, Helfer wie Map/Filter/Reduce) wirklich identisch. – Die Alternative wäre das Duplizieren von Code oder die Verwendung von interface{} + Typzusicherungen (Verlust der Typsicherheit).

Nein wenn:

  • Eine Schnittstelle mit einer oder zwei Methoden reicht aus und ist besser lesbar (io.Reader, fmt.Stringer).
  • Sie parametrisieren „weil Sie es können“: einen einzelnen Anwendungsfall → erstellen Sie eine konkrete Version davon.

Übungen

Übung#go.m10.l3.e1
Versuche: 0Wird geladen…

Definieren Sie die generische Funktion Min[T Ordered](a, b T) T, die mit dem Operator < den kleineren Wert ausgibt. Der Zwang, bestellt zu werden, ist vorbei.

Editor wird geladen…

Lösung nach 3 Versuchen verfügbar

Übung#go.m10.l3.e2
Versuche: 0Wird geladen…

Implementieren Sie Map[T, U any](s []T, f func(T) U) []U: Wenden Sie es auf ein neues Element an und stellen Sie es wieder her.

Editor wird geladen…

Lösung nach 3 Versuchen verfügbar

Quiz#go.m10.l3.e3
Bereit

Eingebaute Qualitätsbeschränkung stimmt dem Bediener == zu, aber der Bediener ist nicht <?

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