1. Generic이란?
Generic(제네릭)은 코드의 재사용성과 유연성을 극대화하기 위해 사용.
특정 타입에 국한되지 않고, 다양한 타입에서 동작할 수 있는 코드를 작성할 수 있도록 함.
Generic을 사용하면 코드의 중복을 줄이고, 타입 안정성을 유지하면서도 유연한 구조를 구현이 가능.
2. Generic의 동작 원리
Generic은 타입을 플레이스홀더로 정의하여, 실제 타입은 코드가 호출될 때 결정.
이렇게 하면 동일한 로직을 여러 타입에 대해 중복 작성할 필요가 없음.
3. Generic의 사용 예제
Generic 타입은 꺾쇠 괄호(<>)를 사용하여 선언.
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
swapValues(&x, &y)
print(x, y) // 20, 10
var str1 = "Hello"
var str2 = "World"
swapValues(&str1, &str2)
print(str1, str2) // "World", "Hello"
- 여기서 T는 타입 매개변수로, 함수 호출 시 결정.
4. Generic을 사용하는 이유
- 코드 중복 감소
- 여러 타입에 대해 동일한 로직을 중복 작성하지 않아도 됨.
- 타입 안정성
- 컴파일 시점에 타입을 검증하므로, 런타임 오류를 방지할 수 있음.
- 유연성
- 다양한 타입에서 동작할 수 있는 코드 작성이 가능.
5. Generic 타입
Generic은 클래스, 구조체, 열거형에서도 사용할 수 있음.
Generic 클래스와 구조체
struct Stack<T> {
private var elements: [T] = []
mutating func push(_ item: T) {
elements.append(item)
}
mutating func pop() -> T? {
return elements.popLast()
}
}
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
print(intStack.pop()) // 2
var stringStack = Stack<String>()
stringStack.push("Swift")
print(stringStack.pop()) // "Swift"
Generic 열거형
enum Result<T> {
case success(T)
case failure(Error)
}
let successResult: Result<String> = .success("Data loaded")
let failureResult: Result<String> = .failure(NSError(domain: "", code: -1, userInfo: nil))
6. Generic 타입 제약 (Type Constraints)
Generic 타입을 사용할 때, 특정 프로토콜을 준수하는 타입으로 제한할 수 있음.
func printElements<T: Collection>(_ collection: T) {
for element in collection {
print(element)
}
}
printElements([1, 2, 3]) // 배열
- T: Collection은 T가 반드시 Collection 프로토콜을 준수해야 함을 나타냄.
7. Generic과 Protocol
Generic은 프로토콜과 함께 사용되어 더 강력한 기능을 제공.
프로토콜에서 제네릭을 사용하려면 연관 타입(Associated Type)을 선언.
protocol Container {
associatedtype Item
var items: [Item] { get set }
mutating func addItem(_ item: Item)
}
struct IntContainer: Container {
var items = [Int]()
mutating func addItem(_ item: Int) {
items.append(item)
}
}
struct StringContainer: Container {
var items = [String]()
mutating func addItem(_ item: String) {
items.append(item)
}
}
8. Generic의 고급 기능
타입 제약을 결합하기
여러 제약 조건을 결합하여 특정 타입을 제한할 수 있음.
func compareValues<T: Equatable & Comparable>(_ a: T, _ b: T) -> Bool {
return a == b
}
타입 매개변수와 연관 타입 사용
타입 매개변수와 연관 타입을 함께 사용 가능.
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
let index = findIndex(of: 5, in: [1, 2, 3, 4, 5])
print(index) // 4
9. 언제 Generic을 사용할까?
- 여러 타입에 대해 동일한 로직을 구현할 때.
- 타입 안정성과 유연성을 동시에 유지하고 싶을 때.
- 코드 중복을 줄이고, 가독성을 높이고 싶을 때.
10. 결론
Generic은 Swift의 강력한 기능 중 하나로, 타입 안정성과 코드 재사용성을 극대화.
Generic을 적절히 활용하면 더욱 유연하고 효율적인 코드를 작성할 수 있음.
다만, 과도한 Generic 사용은 가독성을 저하시킬 수 있으므로, 필요한 경우에만 사용하는 것이 좋음.
'Swift' 카테고리의 다른 글
[Swift] associatedtype (0) | 2024.04.18 |
---|---|
[Swift] Protocol (0) | 2024.04.18 |
[Swift] GCD vs Swift Concurrency: 스레드 관리와 성능 (0) | 2024.04.15 |
[Swift] Swift Concurrency (0) | 2024.04.15 |
[Swift] GCD (0) | 2024.04.13 |