Menu
Coddy logo textTech

Workaround für generische Methoden

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

Go hat eine bemerkenswerte Einschränkung: Du kannst keine Methoden mit eigenen Typparametern für eine Struktur definieren. Obwohl generische Strukturen hervorragend funktionieren, ist das Hinzufügen einer Methode, die einen neuen Typparameter einführt, nicht erlaubt.

Dieser Code lässt sich nicht kompilieren:

type Box[T any] struct {
    Value T
}

// ERROR: Methode darf keine Typparameter haben
func (b Box[T]) Convert[U any](fn func(T) U) U {
    return fn(b.Value)
}

Die Problemumgehung besteht darin, eine eigenständige generische Funktion anstelle einer Methode zu verwenden:

type Box[T any] struct {
    Value T
}

// Reguläre Methode - verwendet den Typparameter der Struktur
func (b Box[T]) Get() T {
    return b.Value
}

// Eigenständige Funktion - kann eigene Typparameter haben
func Convert[T, U any](b Box[T], fn func(T) U) U {
    return fn(b.Value)
}

func main() {
    intBox := Box[int]{Value: 42}
    
    // Verwende die Methode
    fmt.Println(intBox.Get())  // 42
    
    // Verwende die eigenständige Funktion
    strResult := Convert(intBox, func(n int) string {
        return fmt.Sprintf("Number: %d", n)
    })
    fmt.Println(strResult)  // Number: 42
}

Methoden für generische Strukturen können weiterhin den Typparameter T der Struktur verwenden. Die Einschränkung gilt nur für das Einführen zusätzlicher Typparameter in der Methodensignatur.

Wenn du diese Flexibilität benötigst, erzielt eine generische Funktion, die die Struktur als ihr erstes Argument übernimmt, dasselbe Ergebnis.

challenge icon

Aufgabe

Einfach

Erstelle ein Toolkit zur Datentransformation, das die Umgehung der Einschränkung von Go bei generischen Methoden demonstriert! Da Methoden keine neuen Typparameter einführen können, erstellst du eigenständige generische Funktionen, die dieselbe Flexibilität bieten.

Du organisierst deinen Code auf zwei Dateien verteilt:

  • wrapper.go: Definiere deinen generischen Container und die Transformationsfunktionen.

    Erstelle eine generische Struktur Wrapper[T any] mit einem einzigen Feld Value vom Typ T.

    Füge Get eine Methode Wrapper[T] hinzu, die den verpackten Wert zurückgibt. Diese Methode verwendet den Typparameter der Struktur, was zulässig ist.

    Erstelle eine eigenständige generische Funktion Transform[T, U any](w Wrapper[T], fn func(T) U) U, die die Transformationsfunktion auf den Wert des Wrappers anwendet und das Ergebnis zurückgibt. Diese Funktion benötigt einen eigenen Typparameter U für den Ausgabetyp. Deshalb muss sie eine eigenständige Funktion und keine Methode sein.

    Erstelle eine weitere eigenständige Funktion TransformToString[T any](w Wrapper[T]) string, die den verpackten Wert mithilfe von fmt.Sprintf("%v", ...) in eine Zeichenfolge umwandelt.

  • main.go: Demonstriere das Umgehungsmuster mit verschiedenen Transformationen.

    Lies einen Typindikator (int oder string) und anschließend einen Wert ein. Erstelle einen Wrapper des entsprechenden Typs.

    Bei der Eingabe int: Erstelle einen Wrapper[int], verwende anschließend Transform, um den Wert zu verdoppeln (wobei ein int zurückgegeben wird), und verwende TransformToString, um eine Zeichenfolgendarstellung zu erhalten.

    Bei der Eingabe string: Erstelle einen Wrapper[string], verwende anschließend Transform, um die Länge der Zeichenfolge zu ermitteln (wobei ein int zurückgegeben wird), und verwende TransformToString, um die Zeichenfolgendarstellung zu erhalten.

    Gib die Ergebnisse in diesem Format aus:

    Original: [value]
    Transformed: [transformed value]
    As String: [string representation]

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Typindikator (int oder string)
  • Zeile 2: Der Wert

Beispielsweise bei:

int
25

deine Ausgabe sollte folgendermaßen aussehen:

Original: 25
Transformed: 50
As String: 25

Und bei:

string
Hello World

deine Ausgabe sollte folgendermaßen aussehen:

Original: Hello World
Transformed: 11
As String: Hello World

Die wichtigste Erkenntnis ist, dass Transform einen Wrapper[T] als erstes Argument entgegennimmt und einen neuen Typparameter U für den Rückgabetyp einführt. Das wäre als Methode der Struktur nicht möglich. Dieses Muster bietet dir die Flexibilität generischer Transformationen und berücksichtigt gleichzeitig die Einschränkungen des Typsystems von Go.

Probier es selbst

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Typindikator lesen
	typeIndicator, _ := reader.ReadString('\n')
	typeIndicator = strings.TrimSpace(typeIndicator)
	
	// Wert lesen
	value, _ := reader.ReadString('\n')
	value = strings.TrimSpace(value)
	
	if typeIndicator == "int" {
		// Den Integer-Wert parsen
		num, _ := strconv.Atoi(value)
		
		// TODO: Erstelle einen Wrapper[int] mit der geparsten Zahl
		
		// TODO: Verwende Transform, um den Wert zu verdoppeln (Hinweis: übergebe eine Funktion, die verdoppelt)
		
		// TODO: Verwende TransformToString, um die String-Darstellung zu erhalten
		
		// TODO: Gib die Ergebnisse im erforderlichen Format aus:
		// Original: [value]
		// Transformed: [transformed value]
		// As String: [string representation]
		
		_ = num // Entferne diese Zeile, wenn du num verwendest
	} else if typeIndicator == "string" {
		// TODO: Erstelle einen Wrapper[string] mit dem Wert
		
		// TODO: Verwende Transform, um die Länge des Strings zu erhalten (Hinweis: übergib eine Funktion, die len() zurückgibt)
		
		// TODO: Verwende TransformToString, um die String-Darstellung zu erhalten
		
		// TODO: Gib die Ergebnisse im erforderlichen Format aus:
		// Original: [value]
		// Transformed: [transformed value]
		// As String: [string representation]
		
		_ = value // Entferne diese Zeile, wenn du value verwendest
	}
}
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