[swift] Generic(제네릭)
Generic
- ‘포괄적인’이라는 뜻을 가지고 있다.
- 타입에 유연하게 대처할 수 있다.
- 제네릭으로 구현한 기능과 타입은 재사용에 용이하다.
- 코드의 중복을 줄일 수 있어 가독성이 좋다.
Generic을 사용하지 않았을 때
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let tempA = a
a = b
b = tempA
}
위 경우에서는 파라미터 모두 Int형일 경우 문제가 없지만 타입이 다른 경우 사용할 수 없다
그렇기에 다른 타입(예시로 Double, String)일 경우에도 사용하려면 다음과 같이 코드를 작성해야 한다.
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let tempA = a
a = b
b = tempA
}
func swapTwoInts(_ a: inout Double, _ b: inout Double) {
let tempA = a
a = b
b = tempA
}
func swapTwoInts(_ a: inout String, _ b: inout String) {
let tempA = a
a = b
b = tempA
}
이렇게 코드가 굉장히 길어질 수 있기 때문에 Generic을 사용하는 것이다.
Generic Function
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let tempA = a
a = b
b = tempA
}
이런식으로 코드를 작성할 경우 ‘<’, ‘>’를 사용하여 안에 타입처럼 사용할 이름(T)을 선언해 주면 그 뒤로 해당 이름을 타입처럼 사용할 수 있다.
여기서 이 T를 Type Parameter라 하는데, T라는 새로운 형식이 생성되는 것이 아닌, 실제 함수가 호출될 때 해당 매개변수의 타입으로 대체되는 Placeholder이다.
Generic Type
제네릭은 함수에서만 사용 가능한 것이 아니라 구조체, 클래스, 열거형 타입에서도 선언할 수 있다.
struct Stack<T> {
let items: [T] = []
mutating func push(_ item: T) { ... }
mutating func pop() -> T { ... }
}
이렇게 제네릭 타입으로 Stack을 선언할 수 있다.
Type Constraints
제네릭 함수와 타입을 사용할 때 특정 클래스의 하위 클래스나, 특정 프로토콜을 준수하는 타입만 받을 수 있게 제약을 둘 수 있다.
Protocol Constraints
func isSameValues<T>(_ a: T, _ b: T) -> Bool {
return a == b
}
이렇게 선언하면 에러가 난다.
== 이란 a와 b의 타입이 Equatable이란 프로토콜을 준수할 때만 사용할 수 있기 때문이다.
따라서 이 때는 T: Equatable 이라는 제약을 줄 수 있다.
func isSameValues<T: Equatable>(_ a: T, _ b: T) -> Bool {
return a == b
}
Class Constraints
프로토콜 제약과 똑같지만, 해당 자리에 프로토콜이 아닌 클래스 이름이 오는 것이다.
class Bird { }
class Human { }
class Teacher: Human { }
func printName<T: Human>(_ a: T) { }
이렇게 T: Human 클래스 이름을 작성하면
let bird = Bird.init()
let human = Human.init()
let teacher = Teacher.init()
printName(bird) // Global function 'printName' requires that 'Bird' inherit from 'Human'
printName(human)
printName(teacher)
Human 클래스 인스터인스인 Human, Human 클래스를 상속 받은(Subclass) teacher은 printName이란 제네릭 함수를 싱핼시킬 수 있지만, Human 클래스의 Subclass가 아닌 bird 인스턴스는 실행할 수 없다.