Menu
Coddy logo textTech

제네릭 메서드 우회 방법

Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 107개 중 71번째.

Go에는 주목할 만한 제한이 있습니다. 구조체에 고유한 타입 매개변수를 사용하는 메서드를 정의할 수 없습니다. 제네릭 구조체는 훌륭하게 작동하지만, 새로운 타입 매개변수를 introduces하는 메서드를 추가하는 것은 허용되지 않습니다.

이 코드는 컴파일되지 않습니다:

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 function을 사용하는 것입니다:

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
}

generic 구조체의 메서드는 여전히 구조체의 타입 매개변수 T를 사용할 수 있습니다. 이 제한은 메서드 시그니처에 추가 타입 매개변수를 도입하는 경우에만 적용됩니다.

그러한 유연성이 필요할 때는 구조체를 첫 번째 인수로 받는 generic function이 동일한 결과를 얻습니다.

challenge icon

챌린지

쉬움

Go의 제네릭 메서드 제한에 대한 우회 방법을 보여 주는 데이터 변환 toolkit을 만들어 봅시다! 메서드는 새로운 type parameters를 도입할 수 없으므로, 동일한 유연성을 제공하는 독립적인 generic functions를 만들 것입니다.

코드를 두 개의 파일로 구성합니다:

  • wrapper.go: generic container와 변환 functions를 Define합니다.

    단일 field Value를 가지며 type이 T인 generic struct Wrapper[T any]를 Create합니다.

    래핑된 값을 Returns하는 Wrapper[T]의 method Get을 Add합니다. 이 method는 struct의 type parameter를 사용하는데, 이는 allowed됩니다.

    래퍼의 값에 변환 function을 적용하고 results를 Returns하는 독립적인 generic function Transform[T, U any](w Wrapper[T], fn func(T) U) U를 Create합니다. 이 function은 output type을 위한 자체 type parameter U가 필요하므로 method가 아니라 독립적인 function이어야 합니다.

    fmt.Sprintf("%v", ...)를 사용하여 래핑된 값을 string으로 변환하는 또 다른 독립적인 function TransformToString[T any](w Wrapper[T]) string을 Create합니다.

  • main.go: 서로 다른 변환을 사용하여 우회 패턴을 시연합니다.

    type indicator(int 또는 string)를 Read한 다음 값을 Read합니다. 적절한 type의 Wrapper를 Create합니다.

    int 입력의 경우: Wrapper[int]를 Create한 다음 Transform을 사용하여 값을 두 배로 만들고(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

output은 다음과 같아야 합니다:

Original: 25
Transformed: 50
As String: 25

그리고 다음이 주어졌을 때:

string
Hello World

output은 다음과 같아야 합니다:

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

핵심 insight는 Transform이 첫 번째 argument로 Wrapper[T]를 받고 return type을 위한 새로운 type parameter U를 introduces한다는 것입니다. 이는 struct의 method로는 가능하지 않은 작업입니다. 이 패턴을 사용하면 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을 사용하여 값을 두 배로 만들기 (힌트: 두 배로 만드는 함수 전달)
		
		// 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 컴파일러