제네릭(Generic)은 함수, 구조체, 클래스, 열거형에서 사용할 데이터 타입을 외부에서 지정할 수 있게 해주는 기능이다. 그러므로서 코드의 재사용성을 높이고 다양한 타입에 대해 안전하게 동작할 수 있게 한다. 제네릭을 사용하면 특정 타입에 종속되지 않고 조금 더 유연하게 코드를 작성할 수 있다.
func printInt(_ value:Int) {
print(value)
}
func printString(_ value:String) {
print(value)
}
printInt(5)
printString("문자열") // 결과 = 5, 문자열
예를들어 위처럼 정수와 문자열을 출력하는 함수를 따로따로 만들었다.
그러나 제네릭을 사용하면 아래처럼 하나의 함수로 해결이 가능하다
func printAny<T>(_ value: T) {
print(value)
}
printAny(5)
printAny("문자열")` // 결과 = 5, 문자열
이렇게 하면 코드 중복을 줄일 수 있다. T는 어떤 타입이든 올 수 있다는 것을 의미한다.
그리고 T의 자리에 다른 문자를 사용할 수도 있다.
함수 뿐만 아니라 클래스, 구조체, 열거형에서도 사용할 수 있다.
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
var intStack = Stack<Int>()
intStack.push(5)
var stringStack = Stack<String>()
stringStack.push("Hello")
이렇게 하면 정수 스택, 문자열 스택 이외에도 여러 타입의 스택을 하나의 구조체로 만들 수 있다.
그렇다면 매개변수 타입을 그저 Any로 받는 것과의 차이점은 뭐지? - 제네릭 제약
첫째로 타입에 제약을 줄 수가 있다.
func compare<T: Comparable>(_ a: T, _ b: T) -> Bool {
return a < b
}
이 함수는 Comparable 프로토콜을 준수하는 타입만 받을 수 있게되어 있는데 Any는 이런 제약을 줄 수가 없다.
둘째로 타입 정보가 유지된다는 것인데
func doubleGeneric<T: Numeric>(_ value: T) -> T {
return value * 2
}
func doubleAny(_ value: Any) -> Any {
if let intValue = value as? Int {
return intValue * 2
} else if let doubleValue = value as? Double {
return doubleValue * 2
}
return 0
}
let genericResult = doubleGeneric(5) // Int 타입, 값은 10
let anyResult = doubleAny(5) // Any 타입, 값은 10이지만 Any로 래핑됨
제네릭은 컴파일 시점에 타입을 체크하기 때문에 함수를 호출할 때 사용된 타입이 그대로 유지된다. 그러나 Any는 모든 타입을 받을 수 있지만, 컴파일러가 구체적인 타입 정보를 잃어버린다. 따라서 사용할 때 타입캐스팅이 필요할 수 있다.
제네릭에 대해 더 깊이 이해하기 위해 ..
map
, filter
, reduce
와 같은 고차 함수에 대해 학습한다.제네릭은 코드의 재사용성과 유연성을 극대화하는 중요한 개념이다. 이를 통해 다양한 상황에서 효율적이고 안전한 코드를 작성할 수 있다.