Menu
Coddy logo textTech

ジェネリックメソッドの回避策

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 71/107。

Go には注目すべき制限があります。構造体に独自の型パラメーターを持つメソッドを定義することはできません。generic な構造体は非常にうまく機能しますが、new な型パラメーターをintroducesするメソッドを追加することはallowedではありません。

このコードはコンパイルできません:

type Box[T any] struct {
    Value T
}

// ERROR: メソッドには型パラメータを持たせてはならない
func (b Box[T]) Convert[U any](fn func(T) U) U {
    return fn(b.Value)
}

回避策は、メソッドの代わりに独立したgeneric関数を使用することです。

type Box[T any] struct {
    Value T
}

// 通常のメソッド - 構造体の型パラメータを使用する
func (b Box[T]) Get() T {
    return b.Value
}

// スタンドアロン関数 - 独自の型パラメータを持つことができる
func Convert[T, U any](b Box[T], fn func(T) U) U {
    return fn(b.Value)
}

func main() {
    intBox := Box[int]{Value: 42}
    
    // メソッドを使用する
    fmt.Println(intBox.Get())  // 42
    
    // スタンドアロン関数を使用する
    strResult := Convert(intBox, func(n int) string {
        return fmt.Sprintf("Number: %d", n)
    })
    fmt.Println(strResult)  // Number: 42
}

ジェネリック構造体のメソッドでは、構造体の型パラメーター T を引き続き使用できます。この制限は、メソッドシグネチャで追加の型パラメーターを導入する場合にのみ適用されます。

その柔軟性が必要な場合は、構造体を最初の引数として受け取る generic function で同じ結果を実現できます。

challenge icon

チャレンジ

簡単

Go のジェネリックメソッドの制限に対する回避策を示す、データ変換ツールキットを構築しましょう!メソッドでは新しい型パラメーターを導入できないため、同じ柔軟性を実現する standalone generic function を作成します。

コードを 2 つのファイルに分けて整理します。

  • wrapper.go:generic container と transformation functions を Define します。

    単一のフィールド Value(型 T)を持つ generic struct Wrapper[T any] を作成します。

    ラップされた値を returns する Wrapper[T] の method Get を Add します。この method は struct の type parameter を使用しますが、これは allowed です。

    ラッパーの値に transformation function を適用し、results を returns する standalone generic function Transform[T, U any](w Wrapper[T], fn func(T) U) U を Create します。この function には出力型用の own type parameter U が必要です。そのため、method ではなく standalone function でなければなりません。

    ラップされた値を fmt.Sprintf("%v", ...) を使用して string に変換する、もう 1 つの standalone function TransformToString[T any](w Wrapper[T]) string を Create します。

  • main.go:異なる変換を使って workaround pattern を実演します。

    type indicator(int または string)を Read し、続けて値を Read します。適切な型の Wrapper を Create します。

    int 入力の場合:Wrapper[int] を Create し、Transform を使って値を 2 倍にします(int を Returns)。また、TransformToString を使って string representation を取得します。

    string 入力の場合:Wrapper[string] を Create し、Transform を使って string の length を取得します(int を Returns)。また、TransformToString を使って string representation を取得します。

    results を次の format で Print します。

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

次の入力が提供されます。

  • Line 1:Type indicator(int または string
  • Line 2:値

たとえば、次の入力が与えられた場合:

int
25

出力は次のようになります。

Original: 25
Transformed: 50
As String: 25

また、次の入力が与えられた場合:

string
Hello World

出力は次のようになります。

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

重要なポイントは、Transform が最初の引数として Wrapper[T] を受け取り、return type 用の新しい type parameter U を introduces することです。これは struct の method としては実現できません。この pattern により、Go の type system の制約内で作業しながら、generic transformations の柔軟性を得られます。

自分で試してみよう

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// 型インジケーターを読み取る
	typeIndicator, _ := reader.ReadString('\n')
	typeIndicator = strings.TrimSpace(typeIndicator)
	
	// 値を読み取る
	value, _ := reader.ReadString('\n')
	value = strings.TrimSpace(value)
	
	if typeIndicator == "int" {
		// 整数値を解析する
		num, _ := strconv.Atoi(value)
		
		// TODO: 解析した数値で Wrapper[int] を作成する
		
		// TODO: Transform を使用して値を2倍にする(ヒント: 2倍にする関数を渡す)
		
		// TODO: TransformToString を使用して文字列表現を取得する
		
		// TODO: 結果を指定の形式で出力する:
		// Original: [value]
		// Transformed: [transformed value]
		// As String: [string representation]
		
		_ = num // num を使用したらこの行を削除する
	} else if typeIndicator == "string" {
		// TODO: 値で Wrapper[string] を作成する
		
		// TODO: Transform を使って文字列の長さを取得する(ヒント: len() を返す関数を渡す)
		
		// TODO: TransformToString を使用して文字列表現を取得する
		
		// TODO: 結果を指定の形式で出力する:
		// Original: [value]
		// Transformed: [transformed value]
		// As String: [string representation]
		
		_ = value // value を使ったらこの行を削除する
	}
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ