Generic code를 사용하면 더 유연하고 재사용 가능한 함수와 타입의 코드를 작성할 수 있음
Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define.
import UIKit
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
print("스왑 전: someInt의 값은 \(someInt)이고, anotherInt의 값은 \(anotherInt)입니다.")
// 스왑 전: someInt의 값은 3이고, anotherInt의 값은 107입니다.
swap(&someInt, &anotherInt)
print("스왑 후: someInt의 값은 \(someInt)이고, anotherInt의 값은 \(anotherInt)입니다.")
// 스왑 후: someInt의 값은 107이고, anotherInt의 값은 3입니다.
var someString = "좋아합니다"
var anotherString = "귤을"
print("스왑 전: \(someString) \(anotherString)")
// 스왑 전: 좋아합니다 귤을
swap(&someString, &anotherString)
print("스왑 후: \(someString) \(anotherString)")
// 스왑 후: 귤을 좋아합니다
위의 예시에서 제네릭을 쓰지 않았다면? Int가 들어오면 스왑해주는 함수 따로, String이 들어오면 스왑해주는 함수 따로 매번 만들어줘야 해서 번거로움 😢
제네릭 함수는 실제 타입 이름(Int, String 등)을 써주는 대신 Placeholder(위 함수에서는 T)를 사용함. Placeholder는 타입의 종류를 알려주진 않지만 말 그대로 어떤 타입이라는 것은 알려줌. 즉, 매개변수로 Placeholder 타입이 T인 두 매개변수가 있으므로, 두 매개변수는 같은 타입이라는 정도는 알 수 있음. T의 실제 타입은 함수가 호출되는 그 순간 결정됨.
pushing → 새로운 값이 들어간다
popping → collection의 끝 값 (스택 구조에서 맨 위에 쌓여있는 값)이 제거된다
import UIKit
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
print(stackOfStrings) //Stack<String>(items: ["uno", "dos", "tres", "cuatro"])
let fromTheTop = stackOfStrings.pop()
print(stackOfStrings) // Stack<String>(items: ["uno", "dos", "tres"])
스택 맨 위의 값이 pop한 후 없어진 걸 알 수 있음.
func substractTwoValue<T: BinaryInteger>(_ a: T, _ b: T) -> T {
return a - b
}
substractTwoValue(10, 1) // 9
뺄셈을 하려면 뺄셈 연산자를 사용할 수 있는 타입이어야 연산이 가능함.
즉, T가 실제로 받아들일 수 있는 타입은 뺄셈 연산자를 사용할 수 있는 타입이어야 함.
타입 매개변수인 T의 타입을 BinaryInteger 프로토콜을 준수하는 타입으로 한정해두니 뺄셈 연산이 가능하게 됨.
이처럼 타입 제약은 함수 내부에서 실행해야 할 연산에 따라 적절한 타입을 전달받을 수 있도록 제약을 둘 수 있음.
e.g. someArray라는 Array 인스턴스의 index를 통해 해당 인덱스의 값에 접근하고 싶다면 someArray[index]라고 표현하며, someDictionary라는 Dictionary의 key를 통해 해당 키의 값을 가져오고 싶다면 someDictionary[key]라고 표현하는 것이 바로 subscripts.
struct TimesTable {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])") //18
자료 출처