제네릭(Generics)은 프로그래밍에서 데이터 타입을 일반화하는 방법으로, 코드의 재사용성과 타입 안정성을 높이기 위해 사용됩니다. 제네릭을 통해 동일한 코드 구조를 여러 데이터 타입에 대해 작성할 수 있으며, 컴파일 타임에 타입 검사를 통해 오류를 방지할 수 있습니다.
주요 개념
1. 타입 매개변수(Type Parameters)
2. 타입 제약조건(Type Constraints)
제네릭의 이점
1. 코드 재사용성
2. 타입 안전성
Go언어에서 제네릭은 1.18 버전부터 도입되었습니다.
제네릭 함수 정의는 아래와 같습니다.
package main
import "fmt"
// 제네릭 함수
func Print[T any](value T) {
fmt.Println(value)
}
func main() {
Print(42) // int 타입 인수
Print("Hello") // string 타입 인수
Print(3.14) // float64 타입 인수
}
제네릭 타입의 정의는 아래와 같습니다.
package main
import "fmt"
// 제네릭 타입 정의
type Pair[T any, U any] struct {
First T
Second U
}
func main() {
p1 := Pair[int, string]{First: 1, Second: "one"}
p2 := Pair[string, float64]{First: "pi", Second: 3.14}
fmt.Println(p1)
fmt.Println(p2)
}
제네릭 인터페이스를 정의하여 특정 타입 제약 조건을 지정할 수 있습니다.
package main
import "fmt"
// 제네릭 인터페이스
type Adder[T any] interface {
Add(a, b T) T
}
// int 타입에 대한 Adder 인터페이스 구현
type IntAdder struct{}
func (IntAdder) Add(a, b int) int {
return a + b
}
// float64 타입에 대한 Adder 인터페이스 구현
type FloatAdder struct{}
func (FloatAdder) Add(a, b float64) float64 {
return a + b
}
func main() {
var intAdder Adder[int] = IntAdder{}
var floatAdder Adder[float64] = FloatAdder{}
fmt.Println(intAdder.Add(1, 2)) // 3
fmt.Println(floatAdder.Add(1.1, 2.2)) // 3.3
}
제네릭 타입 제약 조건을 추가하여 특정 인터페이스를 구현하는 타입만 허용할 수 있습니다.
package main
import "fmt"
// 제네릭 타입 제약 조건으로 Number 인터페이스 정의
type Number interface {
int | int64 | float64
}
// 제네릭 함수에 타입 제약 조건 추가
func Sum[T Number](a, b T) T {
return a + b
}
func main() {
fmt.Println(Sum(1, 2)) // 3 (int)
fmt.Println(Sum(int64(1), 2)) // 3 (int64)
fmt.Println(Sum(1.1, 2.2)) // 3.3 (float64)
}
아래 코드는 제네릭 타입, 함수, 그리고 제약 조건을 모두 사용하여 다양한 타입의 값을 처리하고, 제네릭의 강력함과 유연성을 보여주는 예시 입니다.
package main
import "fmt"
// 제네릭 타입 제약 조건으로 Number 인터페이스 정의
type Number interface {
int | int64 | float64
}
// 제네릭 함수에 타입 제약 조건 추가
func Sum[T Number](a, b T) T {
return a + b
}
func main() {
fmt.Println(Sum(1, 2)) // 3 (int)
fmt.Println(Sum(int64(1), 2)) // 3 (int64)
fmt.Println(Sum(1.1, 2.2)) // 3.3 (float64)
}