제네릭 메서드 우회 방법
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를 사용할 수 있습니다. 이 제한은 메서드 시그니처에서 추가적인 타입 매개변수를 도입하는 경우에만 적용됩니다.
그러한 유연성이 필요할 때, 구조체를 첫 번째 인자로 받는 제네릭 함수를 사용하면 동일한 결과를 얻을 수 있습니다.
챌린지
쉬움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핵심 통찰은 Transform이 Wrapper[T]를 첫 번째 인수로 취하고 반환 타입을 위해 새로운 타입 매개변수 U를 도입한다는 것입니다. 이는 구조체의 메서드로는 불가능한 일입니다. 이 패턴은 Go의 타입 시스템 제약 내에서 작업하면서 제네릭 변환의 유연성을 제공합니다.
치트 시트
Go는 제네릭 구조체의 메서드가 자체적인 타입 매개변수를 도입하는 것을 허용하지 않습니다. 메서드는 구조체에 이미 존재하는 타입 매개변수만 사용할 수 있습니다.
이 코드는 컴파일되지 않습니다:
type Box[T any] struct {
Value T
}
// ERROR: method must have no type parameters
func (b Box[T]) Convert[U any](fn func(T) U) U {
return fn(b.Value)
}해결 방법은 메서드 대신 독립적인 제네릭 함수를 사용하는 것입니다:
type Box[T any] struct {
Value T
}
// Regular method - uses the struct's type parameter
func (b Box[T]) Get() T {
return b.Value
}
// Standalone function - can have its own type parameters
func Convert[T, U any](b Box[T], fn func(T) U) U {
return fn(b.Value)
}
func main() {
intBox := Box[int]{Value: 42}
// Use the method
fmt.Println(intBox.Get()) // 42
// Use the standalone function
strResult := Convert(intBox, func(n int) string {
return fmt.Sprintf("Number: %d", n)
})
fmt.Println(strResult) // Number: 42
}제네릭 구조체의 메서드는 구조체의 타입 매개변수를 사용할 수 있습니다. 이 제한은 메서드 시그니처에서 추가적인 타입 매개변수를 도입하는 경우에만 적용됩니다. 그러한 유연성이 필요한 경우에는 구조체를 첫 번째 인수로 받는 제네릭 함수를 사용하세요.
직접 해보기
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를 사용할 때 이 라인을 제거하세요
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서