🔥 제네릭 타입
618자
7분
Go 언어는 제네릭 함수뿐만 아니라 제네릭 타입도 지원한답니다. 타입 매개변수를 사용해서 타입을 매개변수화할 수 있어요. 이는 제네릭 데이터 구조를 구현할 때 유용하게 쓰일 수 있지요.
다음 예제는 임의의 타입의 값을 저장하는 단일 연결 리스트에 대한 간단한 타입 선언을 보여줍니다.
package main // List는 임의의 타입 T의 값을 저장하는 단일 연결 리스트를 나타냅니다. type List[T any] struct { next *List[T] // 다음 노드를 가리키는 포인터 val T // 현재 노드의 값 } func main() { }
go
위 코드에서 List
타입은 타입 매개변수 T
를 받아요. T
는 any
타입으로 선언되어 있어서, 어떤 타입의 값이라도 저장할 수 있습니다.
List
구조체는 두 개의 필드를 가지고 있어요:
next
: 같은 타입의List
를 가리키는 포인터입니다. 다음 노드를 가리켜요.val
: 타입T
의 값을 저장하는 필드입니다. 현재 노드의 값이 되겠죠.
이렇게 제네릭 타입을 사용하면, 여러 타입에 대해 재사용 가능한 데이터 구조를 만들 수 있답니다. 코드 중복을 줄이고 추상화 수준을 높일 수 있어요.
이 리스트 구현에 몇 가지 기능을 추가해 볼게요:
// Append 메서드는 리스트 끝에 새 값을 추가합니다. func (l *List[T]) Append(val T) { // 리스트가 비어있으면 새 노드를 헤드로 설정 if l.next == nil { l.next = &List[T]{val: val} return } // 리스트 끝까지 이동 current := l for current.next != nil { current = current.next } // 새 노드를 끝에 추가 current.next = &List[T]{val: val} } // GetAt 메서드는 주어진 인덱스의 값을 반환합니다. func (l *List[T]) GetAt(index int) (T, bool) { // 리스트 순회 current := l.next for i := 0; current != nil; i++ { if i == index { return current.val, true // 인덱스 찾음 } current = current.next } var zero T return zero, false // 인덱스 없음 } // RemoveAt 메서드는 주어진 인덱스의 노드를 제거합니다. func (l *List[T]) RemoveAt(index int) bool { // 리스트가 비어있으면 실패 if l.next == nil { return false } // 헤드 제거 if index == 0 { l.next = l.next.next return true } // 이전 노드 찾기 current := l for i := 0; current.next != nil; i++ { if i == index-1 { if current.next.next == nil { current.next = nil // 끝 노드 제거 } else { current.next = current.next.next // 중간 노드 제거 } return true } current = current.next } return false // 인덱스 없음 } // Print 메서드는 리스트의 모든 값을 출력합니다. func (l *List[T]) Print() { current := l.next for current != nil { fmt.Printf("%v -> ", current.val) current = current.next } fmt.Println("nil") } func main() { // 정수 리스트 생성 intList := &List[int]{} // 리스트에 값 추가 intList.Append(1) intList.Append(2) intList.Append(3) // 리스트 출력 fmt.Print("정수 리스트: ") intList.Print() // 인덱스 1 위치에 있는 값 출력 fmt.Println(intList.GetAt(1)) // 인덱스 1 위치에 있는 값 삭제 intList.RemoveAt(1) // 리스트 출력 intList.Print() // 문자열 리스트 생성 strList := &List[string]{} // 리스트에 값 추가 strList.Append("Hello") strList.Append("World") // 리스트 출력 fmt.Print("문자열 리스트: ") strList.Print() }
go
Append
메서드는 리스트 끝에 새 값을 추가합니다.- 리스트가 비어있으면 새 노드를 헤드로 설정하고,
- 그렇지 않으면 리스트 끝까지 이동한 후 새 노드를 연결하죠.
GetAt
메서드는 주어진 인덱스의 값을 반환합니다.- 리스트를 순회하며 해당 인덱스의 노드를 찾아요.
- 인덱스를 찾으면 값과 함께
true
를 반환하고, - 찾지 못하면 제로값과
false
를 반환합니다.
RemoveAt
메서드는 주어진 인덱스의 노드를 제거합니다.- 리스트가 비어있으면 실패하고,
- 인덱스가 0이면 헤드를 다음 노드로 업데이트 하죠.
- 이전 노드를 찾아 연결을 조정하여 노드를 제거해요.
- 성공하면
true
, 실패하면false
를 반환합니다.
이 코드를 실행하면 다음과 같은 출력을 볼 수 있어요:
정수 리스트: 1 -> 2 -> 3 -> nil 2 true 2 -> 3 -> nil 문자열 리스트: Hello -> World -> nil
text
제네릭 타입 List
를 사용해서 정수 리스트와 문자열 리스트를 모두 생성하고 사용할 수 있음을 확인할 수 있답니다. 같은 코드로 다양한 타입의 리스트를 다룰 수 있으니 편리하죠? 제네릭을 활용하면 이렇게 코드의 재사용성을 높이고 중복을 줄일 수 있습니다.