- 타입에 의존하지 않는 범용 코드를 작성할 때 사용
- 중복을 피하고, 코드를 유연하게 작성할 수 있다.
- Array / Dictionary 도 제네릭 타입이다.
- swift에게 <>를 사용함으로써,
“T는 새로운 타입이 아니야! 이 타입이 존재하는지 찾지마”
라고 말해주는 것- <> 내부에는 타입 매개변수 이름을 지정해줄 수 있는데, 제네릭 함수와 타입 매개변수와의 관계를 더 명확하게 표현해 줄 수 있는 이름을 넣는다.
- 특별히 관계의 의미를 표현하기 어려울 때는, 관용적으로
T
/U
/V
등 대문자 한글자로 표현한다.
✅ 제네릭 타입
제네릭 타입을 구현하면 구조체, 클래스, 열거형 등이 어떤 타입과도 연관되어 동작한다. (Array / Dictionary가 모든 타입을 요소로 삼을 수 있는 이유)
아래의 예시를 보면 Element
를 제네릭으로 선언해주어, 그 안에 모든 타입을 넣을 수 있는걸 확인할 수 있다.
struct Stack<Element> {
var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
var doubleStack = Stack<Double>()
doubleStack.push(item: 1.0)
doubleStack.push(item: 2.0)
print(doubleStack.items) // [1.0, 2.0]
doubleStack.pop() // [1.0]
var strStack = Stack<String>()
strStack.push(item: "123")
strStack.push(item: "456")
print(strStack.items) // ["123", "456"]
var anyStack = Stack<Any>()
anyStack.push(item: "여기엔 아무거나 들어올 수 있어요")
anyStack.push(item: 1.2)
anyStack.push(item: 10)
anyStack.push(item: "이렇게")
print(anyStack.items) // ["여기엔 아무거나 들어올 수 있어요", 1.2, 10, "이렇게"]
✅ 타입 제약
struct Stack<Element: Int> {
var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
✅ 프로토콜 연관 타입(AssociatedType)
종류는 알 수 없으나, 어떤 타입이 여기 쓰일것이다
라고 표현해주는 것 protocol Container {
associatedtype ItemType
// 존재하지 않는 타입인 ItemType 를 연관타입으로 정의해 프로토콜 정의 시 타입 이름으로 사용함
var count: Int {get}
mutating func append(_ item: ItemType)
subscript(_ index: Int) -> ItemType {get}
}
// ItemType 대신 실제 타입인 Int로 구현
class MyContainer: Container {
var items = [Int]()
var count: Int {
return items.count
}
func append(_ item: Int) {
items.append(item)
subscript(index: Int) -> Int {
return items[i]
}
}
}