Menu
Coddy logo textTech

제네릭 메서드 우회 방법

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

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

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

type Box[T any] struct {
    Value T
}

// 오류: 메서드에는 타입 매개변수가 없어야 합니다
func (b Box[T]) Convert[U any](fn func(T) U) U {
    return fn(b.Value)
}

해결 방법은 메서드 대신 독립적인 제네릭 함수를 사용하는 것입니다:

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를 사용할 수 있습니다. 이 제한은 메서드 시그니처에서 추가적인 타입 매개변수를 도입하는 경우에만 적용됩니다.

그러한 유연성이 필요할 때, 구조체를 첫 번째 인자로 받는 제네릭 함수를 사용하면 동일한 결과를 얻을 수 있습니다.

challenge icon

챌린지

쉬움

Go의 제네릭 메서드 제한 사항에 대한 해결 방법을 보여주는 데이터 변환 툴킷을 만들어 봅시다! 메서드는 새로운 타입 매개변수를 도입할 수 없으므로, 동일한 유연성을 제공하는 독립형 제네릭 함수를 만들게 됩니다.

코드는 두 개의 파일로 구성됩니다:

  • wrapper.go: 제네릭 컨테이너와 변환 함수를 정의합니다.

    T 타입의 단일 필드 Value를 가진 제네릭 구조체 Wrapper[T any]를 생성합니다.

    래핑된 값을 반환하는 Get 메서드를 Wrapper[T]에 추가합니다. 이 메서드는 구조체의 타입 매개변수를 사용하며, 이는 허용됩니다.

    변환 함수를 래퍼의 값에 적용하고 결과를 반환하는 독립형 제네릭 함수 Transform[T, U any](w Wrapper[T], fn func(T) U) U를 생성합니다. 이 함수는 출력 타입을 위한 자체 타입 매개변수 U가 필요하며, 이것이 메서드가 아닌 독립형 함수여야 하는 이유입니다.

    fmt.Sprintf("%v", ...)를 사용하여 래핑된 값을 문자열로 변환하는 또 다른 독립형 함수 TransformToString[T any](w Wrapper[T]) string을 생성합니다.

  • main.go: 다양한 변환을 통해 해결 방법 패턴을 시연합니다.

    타입 표시자(int 또는 string)를 읽은 다음 값을 읽습니다. 적절한 타입의 Wrapper를 생성합니다.

    int 입력의 경우: Wrapper[int]를 생성한 다음, Transform을 사용하여 값을 두 배로 만들고(int 반환), TransformToString을 사용하여 문자열 표현을 얻습니다.

    string 입력의 경우: Wrapper[string]을 생성한 다음, Transform을 사용하여 문자열의 길이를 얻고(int 반환), TransformToString을 사용하여 문자열 표현을 얻습니다.

    결과를 다음 형식으로 출력합니다:

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

다음 입력이 제공됩니다:

  • 1행: 타입 표시자 (int 또는 string)
  • 2행: 값

예를 들어, 다음과 같이 주어지면:

int
25

출력은 다음과 같아야 합니다:

Original: 25
Transformed: 50
As String: 25

그리고 다음과 같이 주어지면:

string
Hello World

출력은 다음과 같아야 합니다:

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

핵심 통찰은 TransformWrapper[T]를 첫 번째 인수로 취하고 반환 타입을 위해 새로운 타입 매개변수 U를 도입한다는 것입니다. 이는 구조체의 메서드로는 불가능한 일입니다. 이 패턴은 Go의 타입 시스템 제약 내에서 작업하면서 제네릭 변환의 유연성을 제공합니다.

직접 해보기

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실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨