오늘은 스위프트의 문법 중 매우 중요한 요소 중 하나인 제네릭에 대해 알아보았다.
사실 아직 제대로 사용해본 적은 많이 없지만, 이번 기회를 통해 많이 사용해봐야겠다.
제네릭을 사용하고자 하는 타입 이름 <타입 매개변수> (함수의 매개변수 ...)
func swapTwoValues<T>(_ a: inout T, _ b: inout T) { //둘 다 T로 정의했기 때문에, 타입이 같음
let tmpA:T = a
a = b
b = tmpA
}
print("\(numberOne), \(numberTwo)") // Ok
print("\(stringOne), \(stringOne)") // Ok
//string이나 Int가 둘 다 가능함
print("\(stringOne), \(numberOne)") // 에러 -> 둘의 타입이 다르기 때문에
제네릭 타입
//제네릭을 사용하지 않은 IntStack
struct IntStack {
var items = [Int]()
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
//제네릭을 사용한 Stack
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
extension Stack { //원래 정의는 Stack<Element>로 되어있지만, 정의하지 않음. 하지만 기존에 정의한 Element 사용 가능
var topElement: Element? {
return self.items.last
}
}
타입 제약
struct Stack<Element: Hashable> {
}
//BinaryInteger 프로토콜을 준수하는 타입으로 연산을 제한함
func substractTwoValue<T: BinaryInterger>(_ a: T, _ b: T) -> T {
return a - b
}
프로토콜 연관 타입
protocol Container {
associatedtype ItemType
var count: Int { get }
mutating func append(_ item: ItemType)
subscript(i: Int) -> ItemType { get }
}
//Container에서는 타입에 대해 지정해주지 않았는데, MyContainer에서는 지정을 해줌
//연관 타입 ItemType 대신에 실제 타입인 Int 타입으로 구현
class MyContainer: Container {
var items: Array<Int> = Array<Int>()
var count: Int {
return items.count
}
func append(_ item: Int) {
items.append(item)
}
subscript(i: Int) -> Int {
return items[i]
}
}
제네릭 서브스크립트