Pixel-Shot - stock.adobe.com

Generics in Golang für schnellere und elegantere Codes

Generics sind recht neue Ergänzung der stark und statisch typisierten Sprache Go. Lernen Sie die Vorteile kennen und erfahren Sie, wie der Einstieg mit Generics gelingt.

Go, auch bekannt als Golang, ist eine kompilierte Programmiersprache, die als stark und statisch typisiert gilt. Die Sprache hat im März 2022 die Unterstützung von Generics hinzugefügt, ein Update, das die gesamte Codebasis eines DevOps-Projekts vereinfachen kann.

Bevor Sie sich jedoch mit den Generics von Golang beschäftigen, sollten Sie das Typsystem von Go verstehen und wie es sich in das Universum der Programmiersprachen einfügt.

Stark typisierte vs. schwach typisierte Programmiersprachen

Stark typisierte Programmiersprachen erlauben nur Operationen auf bestimmten Variablentypen. Eine stark typisierte Sprache würde zum Beispiel nicht zulassen, dass eine ganze Zahl zu einer Zeichenkette hinzugefügt wird, da die Sprache die beiden Datentypen als inkompatibel betrachtet.

Eine schwach typisierte Sprache hingegen würde eine der Variablen automatisch in den anderen Typ konvertieren und die Addition durchführen.

Betrachten Sie als Beispiel die folgende Go-Codezeile:

var s = "1" + 1

Da die Typen nicht übereinstimmen – "1" ist eine Zeichenkette und 1 ist eine ganze Zahl – kann Go die Addition nicht durchführen. Stattdessen gibt der Compiler den folgenden Fehler zurück.

invalid operation: "1" + 1 (mismatched types untyped string and untyped int)

Vergleichen Sie diese Ausgabe mit den Ergebnissen der gleichen Operation in JavaScript, einer schwach typisierten Sprache.

console.log("1" + 1)

Dies gibt die Zeichenkette 11 zurück, da JavaScript die Ganzzahl in eine Zeichenkette umwandelt, um die beiden Werte zu verketten. JavaScript verfügt nicht über eine starke Definition der bereitgestellten Datentypen und wandelt sie stattdessen implizit um.

Die Frage, ob eine Sprache stark oder schwach typisiert ist, ist nicht immer einfach zu beantworten. Keine Sprache ist zu 100 Prozent stark oder schwach typisiert, da Merkmale individuell stark oder schwach typisiert sein können.

In den meisten Fällen wird Go aus dem oben genannten Grund als stark typisiert beschrieben. Es gibt jedoch auch Beispiele für schwach typisierte Funktionen in Go. Insbesondere werden Go-Schnittstellen implizit implementiert, ohne das Schlüsselwort implements. Wenn ein Typ Methoden implementiert, die durch eine Schnittstelle definiert sind, dann implementiert er diese Schnittstelle automatisch.

Statisch typisierte versus dynamisch typisierte Programmiersprachen

Statische und dynamische Typisierung beschreiben, ob eine Programmiersprache den Typ einer Variablen zur Kompilierzeit oder zur Laufzeit überprüft. Ein weit verbreiteter Irrglaube ist, dass alle stark typisierten Sprachen statisch typisiert sind und alle schwach typisierten Sprachen dynamisch typisiert sind.

Abbildung 1: Statisch typisierte Sprachen prüfen den Typ einer Variablen zu einem frühen Zeitpunkt im Programmierungszyklus, zur Kompilierzeit, während dynamisch typisierte Sprachen den Typ zur Laufzeit aktualisieren können.
Abbildung 1: Statisch typisierte Sprachen prüfen den Typ einer Variablen zu einem frühen Zeitpunkt im Programmierungszyklus, zur Kompilierzeit, während dynamisch typisierte Sprachen den Typ zur Laufzeit aktualisieren können.

Go ist statisch typisiert und kann daher den Typ einer Variablen zur Laufzeit nicht aktualisieren. Betrachten Sie das folgende Beispiel.

var s = "Hello, World"

s = 2

Beim Ausführen dieses Codes schlägt der Compiler von Go fehl und gibt den folgenden Fehler zurück.

cannot use 2 (untyped int constant) as string value in assignment

Da die Variable s statisch als String typisiert ist, kann sie nur mit Stringwerten aktualisiert werden.

Es folgt ein Beispiel für eine Typumwandlung in Python, einer dynamisch typisierten Sprache, bei der diese Zeilen keine Probleme bei der Ausführung verursachen.

i = 4

i = "four"

Was sind Generics in der Programmierung?

Aus einer allgemeinen Programmierperspektive sind Generics Funktionen oder Datenstrukturen, die ohne die Definition spezifischer Typen geschrieben werden können. Mit Generics ist es möglich, eine Funktion zu coden, die einen generischen Typ annimmt und sich mit vielen verschiedenen Typen abrufen lässt.

Stellen Sie sich zum Beispiel vor, Sie schreiben eine Funktion, die eine Liste ganzer Zahlen umkehrt. In einer Sprache ohne generische Funktionen müsste der Programmierer für jeden Typ der Liste eine eigene Funktion schreiben. Das Programm würde separate Umkehrfunktionen benötigen, um eine Liste von Ganzzahlen, eine Liste von Zeichenketten und eine Liste von Listen zu verarbeiten.

Das Ziel der Generics ist es, doppelten Code zu vermeiden, der entsteht, wenn dieselben Funktionen für verschiedene Typen ausgeführt werden. Statt separate Umkehrfunktionen für Listen von Ganzzahlen und Listen von Zeichenketten zu definieren, erlauben Generics den Programmierern, eine Funktion für einen generischen Typ zu definieren, die dann auf einer Liste, die aus Werten beliebigen Typs besteht, aufgerufen werden kann.

Golang Generics

Während des größten Teils der Geschichte von Go unterstützte die Sprache keine Generics. Go's Schöpfer und leitende Ingenieure waren anfangs besorgt, dass das Hinzufügen von Generics unnötige Komplexität erzeugen oder zu langsameren Programmen führen würde. Im März 2022 wurden die Generics eingeführt, eine einflussreiche Ergänzung der Sprache.

Im Folgenden finden Sie ein Beispiel für die heutigen Go-Generics in Aktion.

func reverse[T any](myList []T) []T {

    length := len(myList)

        reversedList := make([]T, length)

        for index, element := range myList {

            reversedList[length-index-1] = element

        }

        return reversedList

    }

    func main() {

        primes := []int{2, 3, 5, 7, 11, 13}

        names := []string{"Jack", "Frank", "Petra", "Harlod"}

        fmt.Println(reverse(primes))

        fmt.Println(reverse(names))

    }

Die Signatur der generischen Funktion enthält das Schlüsselwort any, das angibt, dass T – unser generischer Typ – ein beliebiger Typ sein kann. Um festzulegen, dass die Funktion nur bestimmte Typen verwenden kann, zum Beispiel Strings und Integers, schreiben Sie diese Typen anstelle des Schlüsselworts any. Bei einer Funktion, die eine Liste sortiert, schränkt die Verwendung des Schlüsselworts Ordered die Funktion auf Typen ein, die Sie mit den Operatoren >, >=, < und <= vergleichen können.

Erfahren Sie mehr über DevOps