Menu
Coddy logo textTech

Einführung in Generics

Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 67 von 107.

Vor Go 1.18 bedeutete das Schreiben von wiederverwendbarem Code für verschiedene Typen entweder das Duplizieren von Funktionen oder die Verwendung des leeren Interface any mit Type Assertions. Generics lösen dieses Problem, indem sie es ermöglichen, Funktionen und Typen zu schreiben, die mit mehreren Typen funktionieren, während die volle Typsicherheit erhalten bleibt.

Stellen Sie sich vor, Sie suchen den Minimalwert in einem Slice. Ohne Generics würden Sie separate Funktionen für jeden Typ benötigen:

func MinInt(values []int) int {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

func MinFloat64(values []float64) float64 {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

Mit Generics schreiben Sie die Logik einmal unter Verwendung eines Typparameters in eckigen Klammern:

func Min[T int | float64](values []T) T {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

Das [T int | float64] deklariert einen Typparameter T, der entweder int oder float64 sein kann. Jetzt können Sie Min([]int{3, 1, 2}) oder Min([]float64{3.5, 1.2}) mit derselben Funktion aufrufen. Der Compiler prüft die Typen zur Kompilierzeit, sodass Sie Sicherheit ohne Laufzeit-Overhead erhalten.

In den kommenden Lektionen werden wir Typparameter, Constraints und generische Structs im Detail untersuchen.

challenge icon

Aufgabe

Einfach

Lassen Sie uns einen Score-Analysator erstellen, der mithilfe von Generics sowohl mit ganzzahligen als auch mit Gleitkomma-Scores arbeitet! Sie werden eine einzige generische Funktion erstellen, die den Maximalwert in einem Slice finden kann, wodurch doppelter Code für verschiedene numerische Typen vermieden wird.

Sie werden Ihren Code auf zwei Dateien verteilen:

  • analyzer.go: Definieren Sie Ihre generische Analysefunktion.

    Erstellen Sie eine generische Funktion Max[T int | float64](values []T) T, die den Maximalwert in einem Slice findet und zurückgibt. Die Funktion sollte über den Typparameter T sowohl mit den Typen int als auch float64 funktionieren.

    Erstellen Sie außerdem eine generische Funktion Sum[T int | float64](values []T) T, die die Summe aller Werte in einem Slice berechnet und zurückgibt.

  • main.go: Lesen Sie die Eingabe ein und demonstrieren Sie Ihre generischen Funktionen mit verschiedenen Typen.

    Lesen Sie einen Typindikator (int oder float), lesen Sie dann eine Anzahl gefolgt von entsprechend vielen Werten. Parsen Sie die Werte entsprechend dem Typ, rufen Sie sowohl Max als auch Sum mit dem entsprechenden Typ auf und geben Sie die Ergebnisse aus.

    Geben Sie für Ganzzahl-Eingaben Folgendes aus:

    Max (int): [value]
    Sum (int): [value]

    Geben Sie für Float-Eingaben das Ergebnis mit einer Dezimalstelle aus:

    Max (float): [value]
    Sum (float): [value]

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Typindikator (int oder float)
  • Zeile 2: Anzahl der Werte (Ganzzahl)
  • Folgende Zeilen: Ein Wert pro Zeile

Zum Beispiel bei folgender Eingabe:

int
4
15
8
23
11

Sollte Ihre Ausgabe wie folgt aussehen:

Max (int): 23
Sum (int): 57

Und bei folgender Eingabe:

float
3
4.5
9.2
6.8

Sollte Ihre Ausgabe wie folgt aussehen:

Max (float): 9.2
Sum (float): 20.5

Beachten Sie, wie dieselben generischen Funktionen sowohl Ganzzahl- als auch Gleitkommadaten verarbeiten – der Typparameter [T int | float64] ermöglicht es dem Compiler, typsicheren Code für jede Verwendung zu generieren, während Sie die Logik nur einmal schreiben.

Probier es selbst

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Typ-Indikator lesen
	typeStr, _ := reader.ReadString('\n')
	typeStr = strings.TrimSpace(typeStr)

	// Anzahl lesen
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))

	if typeStr == "int" {
		// Ganzzahlwerte lesen
		values := make([]int, count)
		for i := 0; i < count; i++ {
			line, _ := reader.ReadString('\n')
			values[i], _ = strconv.Atoi(strings.TrimSpace(line))
		}

		// TODO: Max und Sum mit integer Slice aufrufen
		// TODO: Ergebnisse im Format "Max (int): [value]" und "Sum (int): [value]" ausgeben

	} else if typeStr == "float" {
		// Fließkommawerte lesen
		values := make([]float64, count)
		for i := 0; i < count; i++ {
			line, _ := reader.ReadString('\n')
			values[i], _ = strconv.ParseFloat(strings.TrimSpace(line), 64)
		}

		// TODO: Max und Sum mit float64 Slice aufrufen
		// TODO: Ergebnisse mit einer Nachkommastelle im Format "Max (float): [value]" und "Sum (float): [value]" ausgeben
	}
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung