Menu
CoddyTech

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 wiederverwendbaren Codes für verschiedene Typen entweder, Funktionen zu duplizieren oder die leere Schnittstelle any mit Typzusicherungen zu verwenden. Generics lösen dieses Problem, indem sie es dir ermöglichen, Funktionen und Typen zu schreiben, die mit mehreren Typen arbeiten und dabei die vollständige Typsicherheit gewährleisten.

Stell dir vor, du möchtest den Minimalwert in einem Slice finden. Ohne Generics bräuchtest du für jeden Typ eine eigene Funktion:

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 schreibst du die Logik einmal und verwendest dabei einen Typparameter 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
}

Die Deklaration [T int | float64] legt einen Typparameter T fest, der entweder int oder float64 sein kann. Jetzt kannst du mit derselben Funktion Min([]int{3, 1, 2}) oder Min([]float64{3.5, 1.2}) aufrufen. Der Compiler prüft die Typen zur Kompilierzeit, sodass du Typsicherheit ohne Laufzeit-Overhead erhältst.

In den kommenden Lektionen beschäftigen wir uns ausführlich mit Typparametern, Einschränkungen und generischen Structs.

challenge icon

Aufgabe

Einfach

Lass uns einen Score-Analysator erstellen, der mithilfe von Generics sowohl mit ganzzahligen als auch mit Gleitkomma-Scores arbeitet! Du erstellst eine einzige generische Funktion, die den Maximalwert in einem Slice finden kann, sodass kein doppelter Code für verschiedene numerische Typen erforderlich ist.

Du verteilst deinen Code auf zwei Dateien:

  • analyzer.go: Definiere deine generische Analysefunktion.

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

    Erstelle 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: Lies die Eingabe ein und führe deine generischen Funktionen mit verschiedenen Typen vor.

    Lies einen Typindikator (int oder float) und anschließend eine Anzahl, gefolgt von entsprechend vielen Werten. Parse die Werte entsprechend dem Typ, rufe Max und Sum mit dem passenden Typ auf und gib die Ergebnisse aus.

    Gib bei einer ganzzahligen Eingabe Folgendes aus:

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

    Gib bei einer Gleitkommaeingabe die Werte mit einer Nachkommastelle 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 deine Ausgabe wie folgt aussehen:

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

Und bei folgender Eingabe:

float
3
4.5
9.2
6.8

sollte deine Ausgabe wie folgt aussehen:

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

Beachte, wie dieselben generischen Funktionen sowohl ganzzahlige als auch Gleitkommadaten verarbeiten: Der Typparameter [T int | float64] ermöglicht es dem Compiler, für jede Verwendung typsicheren Code zu erzeugen, während du die Logik nur einmal schreibst.

Probier es selbst

package main

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

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

	// Typkennzeichen einlesen
	typeStr, _ := reader.ReadString('\n')
	typeStr = strings.TrimSpace(typeStr)

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

	if typeStr == "int" {
		// Ganzzahlwerte einlesen
		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 einem Ganzzahl-Slice aufrufen
		// TODO: Ergebnisse im Format "Max (int): [value]" und "Sum (int): [value]" ausgeben

	} else if typeStr == "float" {
		// Gleitkommawerte einlesen
		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 einem 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

Übe selbstständig: Online-Go-Compiler