제네릭 메서드 우회 방법
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이 동일한 결과를 얻습니다.
챌린지
쉬움Go의 제네릭 메서드 제한에 대한 우회 방법을 보여 주는 데이터 변환 toolkit을 만들어 봅시다! 메서드는 새로운 type parameters를 도입할 수 없으므로, 동일한 유연성을 제공하는 독립적인 generic functions를 만들 것입니다.
코드를 두 개의 파일로 구성합니다:
wrapper.go: generic container와 변환 functions를 Define합니다.단일 field
Value를 가지며 type이T인 generic structWrapper[T any]를 Create합니다.래핑된 값을 Returns하는
Wrapper[T]의 methodGet을 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 parameterU가 필요하므로 method가 아니라 독립적인 function이어야 합니다.fmt.Sprintf("%v", ...)를 사용하여 래핑된 값을 string으로 변환하는 또 다른 독립적인 functionTransformToString[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
25output은 다음과 같아야 합니다:
Original: 25
Transformed: 50
As String: 25그리고 다음이 주어졌을 때:
string
Hello Worldoutput은 다음과 같아야 합니다:
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를 사용할 때 이 줄을 제거하세요
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서직접 연습해 보세요: 온라인 Go 컴파일러