제네릭이란 타입에 의존하지 않는 범용 코드를 작성할 때 사용한다
제네릭을 사용하면 중복을 피하고, 코드를 유연하게 작성할 수 있다
<aside> 💡 제네릭 문법
형식에 (타입에) 관계없이, 한번의 구현으로 모든 타입을 처리하여, 타입에 유연한 함수 작성가능 (유지보수/재사용성 증가)
(함수 뿐만아니라) 구조체 / 클래스 / 열거형도 제네릭으로 일반화 가능
타입 파라미터는 함수 내부에서 파라미터 형식이나 리턴형으로 사용됨 (함수 바디에서 사용하는 것도 가능)
보통은 T를 사용하지만 다른 이름을 사용하는 것도 문제가 없음, 형식이름이기 때문에 Upper Camel case로 선언
2개이상을 선언하는 것도 가능
제네릭은 타입에 관계없이, 하나의 정의(구현)로 모든 타입(자료형)을 처리할 수 있는 문법
제네릭 함수, 제네릭 구조체/클래스
일반 함수와 비교해보면, 작성해야하는 코드의 양이 비약적으로 감소
타입 파라미터는 실제 자료형으로 대체되는 플레이스 홀더(어떤 기호같은것) ===> 새로운 형식이 생성되는 것이 아님
코드가 실행될때 문맥에 따라서 실제 형식으로 대체되는 "플레이스 홀더"일뿐
</aside>
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let tempA = a
a = b
b = tempA
}
위의 a,b 파라미터는 모두 동일한 Int 타입인 경우는 문제가 없음 하지만 각각 Double, String 처럼 다른 타입일 경우 오류 발생 하므로 반드시 같은 타입 끼리만 구현 가능하다는 것
→이럴때 사용하는것이 Generic
T는 Type Parameter 라고 부르는데 새로운 형식이 생성되는 것이 아니라 실제 함수가 호출될 때 해당 매개 변수의 타입으로 대체되는 Placeholder 임 <> 꺽쇠로 감싸는 이유는 새로운 타입이 아니니 이 타입이 존재하는지 찾지말라고 swift에게 말해주기 위함!
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
var numberOne = 10
var numberTwo = 5
var doubleOne = 10.5
var doubleTwo = 5.5
var stringOne = "A"
var stringTwo = "B"
swapTwoValues(&numberOne, &numberTwo)
print("\\(numberOne), \\(numberTwo)") // 5, 10 실제 함수 호출시 T는 Int타입
swapTwoValues(&doubleOne, &doubleTwo)
print("\\(doubleOne), \\(doubleTwo)") // 5.5, 10.5 실제 함수 호출시 T는 Double 타입
swapTwoValues(&stringOne, &stringTwo)
print("\\(stringOne), \\(stringTwo)") // "B", "A" 실제 함수 호출시 T는 String타입
print("\\(numberOne), \\(stringTwo)") // 서로 다른 타입을 파라밑로 전달하기때문에 에러!
이렇게 실제 함수를 호출할때 Type Parameter인 T의 타입이 결정된다.
위 내용처럼 제네릭을 이용한 함수는 제네릭 함수라고 하며, 제네릭은 함수이외에 구조체, 클래스, 열거형 타입에도 선언이 가능하다. 이것을 Generic Type 이라고 한다.
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()
제네릭 타입으로 Stack을 선언해주고, 제네릭 타입의 인스턴스를 생성해주었다. 제네릭 타입을 선언할때 마찬가지로 <>를 통해 어떤 타입으로 사용할 것인지를 명시해줘야한다. → 배열 생성때와 동일!! ( Array가 제네릭 타입이기 때문이다.)
let array1: Array<Int> = .init()
let array2 = Array<Int>.init()
제네릭 함수와 타입을 사용할 때 특정 클래스의 하위 클래스나, 특정 프로토콜을 준수하는 타입만 받을 수 있게 제약을 둘 수 있다.