iOS/Swift
[Swift] Generics 제네릭
최지철
2023. 12. 13. 15:37
반응형
Generics
- 타입에 의존하지 않는 범용코드를 작성할 때, 사용
- 제네릭을 사용하면, 중복을 피하고 코드를 유연하게 작성할 수 있다.
- Swift의 강력한 특징 중 하나이고 Swift 표준 라이브러리 대부분은 제너릭 코드로 되어 있다
- 예를 들어 Swift의 Array 와 Dictionary 타입은 둘다 제너릭 콜렉션
- Int 값을 가진 배열, 또는 String 값을 가진 배열 또는 실제로 Swift에서 생성될 수 있는 다른 모든 타입에 대한 배열을 생성 가능
- 모든 지정된 타입의 값을 저장하기 위한 딕져너리를 생성할 수 있고 해당 타입에 대한 제한은 없다.
Generic Function
- 제너릭이 해결하는 문제
- 아래와 같이 일반적으로 a 와 b 가 같은 타입이 아니면 바꾸는 것은 불가능하다. Swift는 타입 안정성 언어이고 String 타입의 변수와 Double 타입의 변수가 서로 값을 바꾸도록 허락하지 않으며, 이러한 시도는 컴파일 에러가 발생한다.
func swapTwoStrings(_ a: inout String, _ b: inout String) {
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
let temporaryA = a
a = b
b = temporaryA
}
- 제너릭 함수 (Generic functions) 는 모든 타입과 함께 동작할 수 있다.
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
- Int, String, 또는 Double 와 같은 실제 타입 이름 대신에 이 경우 T 라는 임의의 타입 이름을 사용한다. 이 임의의 타입 이름은 T 가 무엇이어야 하는지 아무 말도 하지 않지만 T 가 무엇을 나타내든 a 와 b 는 모두 같은 타입 T여야 한다고 말한다. T 의 실제 타입은 swapTwoValues(_:_:) 함수가 호출될 때마다 결정된다.
- 첫번째 인자가 Int면 두번째 인자도 Int기 때문에 같은 인자의 타입 값을 넣어야한다.
- 제너릭 함수와 제너릭이 아닌 함수 사이의 다른 차이점
- 제너릭 함수의 이름 (swapTwoValues(_:_:))에 바로 임의의 타입 이름 (T)이 꺾쇠 괄호 내 (<T>)에 위치한다는 것 이다. 이 괄호는 T 는 swapTwoValues(_:_:) 함수 정의 내에서 임의의 타입 이름이라고 Swift에게 말한다. T 는 임의의 타입이므로 Swift는 T 라는 실제 타입을 찾지 않는다.
- 제너릭 함수의 이름 (swapTwoValues(_:_:))에 바로 임의의 타입 이름 (T)이 꺾쇠 괄호 내 (<T>)에 위치한다는 것 이다. 이 괄호는 T 는 swapTwoValues(_:_:) 함수 정의 내에서 임의의 타입 이름이라고 Swift에게 말한다. T 는 임의의 타입이므로 Swift는 T 라는 실제 타입을 찾지 않는다.
타입 파라미터 (Type Parameters)
- 예제에서 임의의 타입 T 는 타입 파라미터 (type parameter) 의 예이다.
- 타입 파라미터는 임의의 타입을 지정하고 이름을 지정하며 꺾쇠 괄호 (예: <T>) 사이에 기록하고 함수의 이름 바로 뒤에 작성
- 콤마로 구분된 꺾쇠 괄호 안에 여러개 타입 파라미터를 작성하여 하나 이상의 타입 파라미터를 제공할 수 있다.
- 값이 아니라 타입에 대한 임의의 표시라는 것을 나타내기 위해 항상 타입 파라미터가 주어질 때 대문자 이름 (T와 MyTypeParameter 와 같은)으로 주어진다.
Generic Types
- 제너릭 함수 외에도 Swift는 고유한 제너릭 타입 (generic types) 을 정의할 수 있다.
- Array 와 Dictionary 와 유사한 방식으로 모든 타입 에서 동작할 수 있는 사용자 정의 클래스, 구조체, 그리고 열거형이다.
struct Stack<T> {
let items: [T] = []
mutating func push(_ item: T) { ... }
mutating func pop() -> T { ... }
}
- 제네릭 타입 객체 생성 시,
let stack1: Stack<Int> = .init()
let stack2 = Stack<Int>.init()
Type Constraints
- 제네릭 함수와 타입을 사용할 때, 특정 클래스의 하위 클래스나, 특정 프로토콜을 준수하는 타입만 받을 수 있게 제약 할 수 있다.
프로토콜 제약
func isSameValues<T: Equatable>(_ a: T, _ b: T) -> Bool {
return a == b
}
- Equatable 프로토콜을 준수하는 파라미터만 받을 수 있음
클래스 제약
class Bird { }
class Human { }
class Teacher: Human { }
func printName<T: Human>(_ a: T) { }
오버로딩
- 제네릭 함수도 오버로딩이 가능하다. 만약 오버로딩시, 타입이 지정되어있는 경우, 타입이 지정된 함수가 우선순위가 더 높다
반응형