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, Funktionen zu duplizieren oder die leere Schnittstelle any mit Typ-Assertions zu verwenden. Generics lösen dieses Problem, indem sie dir ermöglichen, Funktionen und Typen zu schreiben, die mit mehreren Typen funktionieren und gleichzeitig vollständige Typsicherheit gewährleisten.

Stell dir vor, du möchtest den kleinsten Wert in einem slice finden. Ohne generics bräuchtest du für jeden Typ separate Funktionen:

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
}

Das [T int | float64] deklariert einen Typparameter T, 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 Sicherheit ohne Laufzeitaufwand erhältst.

In den kommenden Lektionen werden wir Typparameter, Einschränkungen und generische Strukturen ausführlich untersuchen.

challenge icon

Aufgabe

Einfach

Erstellen wir einen Punkteanalysator, der mithilfe von Generics sowohl mit ganzzahligen als auch mit Gleitkommawerten 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 organisierst deinen Code auf zwei Dateien verteilt:

  • 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 soll sowohl mit den Typen int als auch float64 über den Typparameter T 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 demonstriere deine generischen Funktionen mit verschiedenen Typen.

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

    Gib bei ganzzahliger Eingabe Folgendes aus:

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

    Gib bei Gleitkommaeingabe mit einer Nachkommastelle aus:

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

Die folgenden Eingaben werden bereitgestellt:

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

Beispiel für folgende Eingabe:

int
4
15
8
23
11

Deine Ausgabe sollte folgendermaßen aussehen:

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

Beispiel für folgende Eingabe:

float
3
4.5
9.2
6.8

Deine Ausgabe sollte folgendermaßen 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 generieren, während du die Logik nur einmal schreiben musst.

Probier es selbst

package main

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

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

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

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

	if typeStr == "int" {
		// Integer-Werte 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" {
		// Float-Werte 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 Dezimalstelle 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