안녕하세요 🐶
빈 지식 채우기의 비니🙋🏻♂️ 입니다.
오늘은 제네릭 ( Generic ) 대해 알아보는 시간을 가지겠습니다.
1. 개요
제네릭이란 타입에 의존하지 않고 범용 코드를 작성할 때 사용한다.
우리는!! 제네릭을 사용함으로써 코드 중복을 피하고, 유연하게 작성할 수 있다.
흔히 사용하는 Swfit 표준 라이브러리 대다수는 제네릭으로 선언되어 있습니다~
대표적으로 Array 와 Dictionary 가 있찌요!
정확한 제네릭에 대해서는 아래에서 설명을 드리겠습니다~
1-1. 제네릭 함수 ( Generic Function )
아아아주~ 간단하게 두 Int 값을 Print 출력하는 함수가 있고, 아래와 같이 구현해 보았습니다.
func printInts(_ a: Int, _ b: Int) {
print("A값은 \(a), B값은 \(b)")
}
이렇게 하면 두 파라미터가 Int의 경우에는 문제없이 잘 돌아갑니다.
근데 만약 파라미터가 String, Double일 경우엔 어떨까요?! 네~ 물론 사용할 수 없습니다.
왜냐? Swift는 민감한 언어이므로 다른 타입이 돌아가기 위해서는 아래와 같이 따로 구현을 해야 합니다.
func printDoubles(_ a: Double, _ b: Double) {
print("A값은 \(a), B값은 \(b)")
}
func printStrings(_ a: String, _ b: String) {
print("A값은 \(a), B값은 \(b)")
}
뭐.. 이렇게 작성하면 사용할 수는 있습니다.
근데 타입이 추가가 될때마다 또는 형식이 바뀔때마다 오버로딩을 할 수도 있습니다..
너무 귀찬;;
func printTwoValues<T>(_ a: T, _ b: T) { // (1)
print("A값은 \(a), B값은 \(b)")
}
- T ( 제네릭 ) 으로 선언하여 타입에 제한을 두지 않게 사용한다.
여기서 T는 Type Parameter 라고 부르는데,
실제로 새로운 타입이 형성되는 것이 아니라,
실제 함수가 호출될 때 해당 매개변수의 타입으로 대체되는 Placeholder 입니다.
var someInt = 1
var aotherInt = 2
printTwoValues(someInt, aotherInt)
var someString = "Hi"
var aotherString = "Bye"
printTwoValues(someString, aotherString)
위와 같이 제네릭 함수로 선언하여 파라미터 제약을 두지 않고 사용할 수 있습니다!!
다만, 여기서 a와 b를 각각의 다른 타입으로 선언하면 어떻게 될까? 에러가 남
printTwoValues(someInt, someString)
왜냐하면,
이미 첫 번째 파라미터 a의 T가 Int로 결정됐기 때문에,
두 번째 파라미터인 b 타입이 Int가 아니여서 에러가 나기 때문입니다.
똑같은 내용의 함수를 오버로딩 할 필요 없이 제네릭을 사용하면 된다!!
따라서 코드 중복을 피하고 유연하게 코드를 짤 수 있다!!
1-2. 제네릭 타입 ( Generic Type )
구조체, 클래스, 열거형 타입에도 선언이 가능하다.
struct Stack<T> {
let items: [T] = []
mutating func push(_ item: T) { ... }
mutating func pop() -> T { ... }
}
이렇게 T ( Generic ) Type 으로 선언할 수 있습니다.
그럼? 인스턴스는 어떻게 생성해야 하나요?
let stack1: Stack<Int> = .init()
let stack2 = Stack<Int>.init()
<> 를 통해 어떤 타입으로 사용할 것인지 명시해주어야 합니다.
응..? 근데 저거 어디서 많이 봤는데... 바로바로봐로~~~
let array1: Array<Int> = .init()
let array2 = Array<Int>.init()
넵! 개요에서 말한 배열 생성할 때랑 같습니다.
왜나하면 Array는 제네릭 타입으로 선언된 구조체이기 때문입니다~
이처럼 우리는 아무렇지 않게 제네릭을 사용해오고 있었습니다~ ㅎㅎ :)
2. 타입 제약 ( Type Constraints )
제네릭 함수 및 타입을 사용할 때, 특정 타입만 받을 수 있게 제약을 두는 것이다.
제네릭으로 선언된 함수 또는 타입에 사용이 가능합니다.
특정! 프로토콜을 준수하는 타입만 받을 수 있게 제약을 둘 수 있습니다.
2-1. 프로토콜 제약
func CheakSameValues<T>(_ a: T, _ b: T) -> Bool {
return a == b
}
왜나하면 == 연산자는, a와 b 타입이 Equatable 프로토콜을 준수할 때만 사용이 가능합니다.
따라서 우리는 아래와 같이 구현을 해야 합니다!
func CheckSameValues<T: Equatable>(_ a: T, _ b: T) -> Bool {
return a == b
}
이렇게 T: Equatable 이런 식으로 제약을 걸어 Equatable이란 프로토콜을 준수하는 파라미터만 받을 수 있습니다.
3. 제네릭 함수와 오버로딩
만약 특정 타입일 경우, 제네릭으로 선언된 함수 말고 다른 함수로 구현하고 싶으면?
이떄는?! 제네릭 함수를 오버로딩을 하면 됩니다.
func printTwoValues<T>(_ a: T, _ b: T) {
print("A값은 \(a), B값은 \(b)")
}
func printTwoValues(_ a: Int, _ b: Int) {
print("Int A값은 \(a), Int B값은 \(b)")
}
이렇게 할 경우,
타입이 명시된 함수가 제네릭 함수보다 우선순위가 높습니다!
var a = 1
var b = 2
printTwoValues(a, b) // "Int A값은 1, Int B값은 2"
var c = "Hi"
var d = "Bean!"
printTwoValues(c, d) // "A값은 Hi, B값은 Bean"
Int 타입으로 printTwoValues를 실행할 경우, 타입이 명시된 함수가 실행되고,
String 타입으로 printTwovalues를 실행하면, 제네릭 함수가 실행됩니다~~~!!
이상으로 제네릭 ( Generic ) 포스팅을 마치겠습니다.
틀린 부분이나 궁금한 사항은 댓글 남겨주세요~
참고
'iOS📱 > Swift' 카테고리의 다른 글
[ RxSwift ] Observable & Subscribe (0) | 2023.05.09 |
---|---|
[ Swift ] 일급 객체 ( First-Class Object ) (2) | 2023.01.26 |
[ Swift ] KVC(Key-Value-Coding), KVO(Key-Value-Observing) (0) | 2023.01.05 |
[ Swift ] 서브스크립트 ( Subscript ) (0) | 2022.12.30 |
[ Swift ] ARC (2) Retain Cycle, 강한참조, 약한참조 그리고 미소유참조 (0) | 2022.12.29 |