generic이란

제네릭(Generic)이란, 특정 타입에 의존하지 않고 다양한 타입을 지원하는 프로그래밍 기법입니다. 즉, 같은 코드로 여러 타입을 처리할 수 있도록 해주는 기능입니다. 쉽게 말해서 **“하나의 코드로 여러 타입을 다룰 수 있게 해주는 도구”**입니다.

일반적으로 함수나 구조체는 특정 타입에 종속됩니다. 예를 들어, 두 개의 숫자를 더하는 함수를 만든다고 하면 아래와 같습니다.

func AddInt(a, b int) int {
    return a + b
}

func AddFloat(a, b float64) float64 {
    return a + b
}

위처럼 int와 float64를 각각 처리하는 별도의 함수를 만들어야 합니다. 하지만 제네릭을 사용하면 아래와 같이 하나의 코드로 여러 타입을 지원할 수 있습니다.

package main

import "fmt"

// 제네릭을 사용한 함수
// T는 타입 매개변수로, int 또는 float64 타입을 가질 수 있습니다.
func Add[T int | float64](a, b T) T {
    return a + b
}

func main() {
    fmt.Println(Add(3, 5))       // int 타입
    fmt.Println(Add(3.2, 5.8))   // float64 타입
}

generic 핵심 개념

  1. 타입 매개변수 (Type Parameter)
  2. 타입 제약 (Type Constraint)
  3. 장점
  4. 단점

Golang에서의 generic

generic function

// generic을 사용하지 않은 경우
func AddInt(a, b int) int {
    return a + b
}

func AddFloat(a, b float64) float64 {
    return a + b
}

// generic을 사용한 경우
func Add[T int | float64](a, b T) T {
    return a + b
}

func main() {
    fmt.Println(Add(3, 5))       // int 타입
    fmt.Println(Add(3.2, 5.8))   // float64 타입
}

generic struct

package main

import "fmt"

// 제네릭을 지원하는 구조체
type Box[T any] struct {
    value T
}

func main() {
    intBox := Box[int]{value: 10}
    stringBox := Box[string]{value: "Hello"}

    fmt.Println(intBox.value)   // 10
    fmt.Println(stringBox.value) // Hello
}

generic interface

package main

import "fmt"

// 제네릭 인터페이스
type Printer[T any] interface {
    Print(T)
}

// 구조체가 인터페이스를 구현
type ConsolePrinter[T any] struct{}

func (cp ConsolePrinter[T]) Print(value T) {
    fmt.Println(value)
}

func main() {
    intPrinter := ConsolePrinter[int]{}
    intPrinter.Print(42)

    stringPrinter := ConsolePrinter[string]{}
    stringPrinter.Print("Hello, Go!")
}