본문 바로가기
Swift

[Swift] 제네릭 (Generics)

by Sky Titan 2021. 5. 28.
728x90
스위프트 프로그래밍
국내도서
저자 : 야곰
출판 : 한빛미디어 2019.10.01
상세보기

제네릭 (Generics)

  • 함수, 클래스 내부에서 사용할 타입 파라미터를 받아 어떤 타입에도 유연하게 대응하게 작성할 수 있는 스위프트 문법

 

1. 제네릭 함수

  • 함수명 뒤에 '<플레이스홀더>' 와 같은 방식으로 선언이 가능하다.
  • 이 때 플레이스 홀더의 타입은 전달되는 전달 인자에 의해 결정된다.
  • 전달인자의 타입에 따라서 불필요한 추가 함수들을 만들 필요가 없다.
import UIKit


func swap<T>(a: inout T, b: inout T) {
    let temp = a
    a = b
    b = temp
}

var a: Int = 6
var b: Int = 7
print("before a: \(a), b: \(b)")
swap(&a, &b)

print("after a: \(a), b: \(b)")

var c: Double = 3
var d: Double = 4
print("before c: \(c), d: \(d)")
swap(&c, &d)
print("after c: \(c), d: \(d)")


/* 결과
before a: 6, b: 7
after a: 7, b: 6
before c: 3.0, d: 4.0
after c: 4.0, d: 3.0
*/

 

2. 제네릭 타입

  • 사용자 정의 타입 class, struct, enum이 어떤 타입과도 연관되어 동작할 수 있도록한다.
  • EX) array, dictionary가 어떤 타입에도 동작하는 것과 같음
import UIKit

struct Stack<T> {
    private var items: [T] = []
    var count: Int {
        return items.count
    }
    
    
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T{
        return items.removeLast()
    }
}

var stack: Stack = Stack<Int>()

stack.push(item: 1)
stack.push(item: 2)
stack.push(item: 3)
stack.push(item: 4)

for i in 0 ..< stack.count {
    print(stack.pop())
}

/* 결과
4
3
2
1
*/

 

3. 제네릭 타입 확장

  • 제네릭을 사용하는 사용자 정의 타입을 extension을 통해 확장하려고 할 때, extension에 타입 매개변수를 명시하지 않아야함.
  • 원래 제네릭 정의에 명시한 타입 매개변수는 extension에서도 사용가능
import UIKit

struct Stack<T> {
    private var items: [T] = []
    var count: Int {
        return items.count
    }
    
    
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T{
        return items.removeLast()
    }
}
extension Stack {
    
    func first() -> T? {
        return items.first
    }
}

var stack: Stack = Stack<Int>()

stack.push(item: 1)
stack.push(item: 2)
stack.push(item: 3)
stack.push(item: 4)

if let first = stack.first() {
    print("first: \(first)")
}
for i in 0 ..< stack.count {
    print(stack.pop())
}

/*결과
first: 1
4
3
2
1
*/

 

4. 타입 제약

  • 제네릭에 들어갈 타입 파라미터가 특정 타입에 한정되어야 하는 경우 제약을 걸 수 있다.
  • 타입 제약은 'class' 혹은 'protocol'로만 줄 수 있다.
    • struct, enum은 불가능
  • <플레이스홀더: 타입 제약> 과 같은 방식으로 사용가능
  • 제약을 여러 개 추가하고 싶다면 타입 정의 시 where문을 이용하여 where T: BinaryFloating과 같은 방식으로 사용 가능
import UIKit

protocol Number {}
protocol Swappable {}

extension Int: Number, Swappable {
    
}

func swap<T: Number> (a: inout T, b: inout T) where T: Swappable {
    let temp = a
    a = b
    b = temp
}

var a: Int = 10
var b: Int = 5

print("before a: \(a), b: \(b)")
swap(&a, &b)
print("after a: \(a), b: \(b)")

/* 결과
before a: 10, b: 5
after a: 5, b: 10
*/

 

5. 프로토콜의 연관 타입

  • 프로토콜을 정의할 때 연관 타입을 통해, '종류는 알 수 없지만 쓰일 타입' 에대해서 정의해줄 수 있다.
import UIKit

protocol Stack {
    associatedtype T
    var items: [T] {get set}
    
    mutating func push(item: T)
    mutating func pop() -> T
}

struct IntStack: Stack {
    
    var items: [Int] = [] //자연스럽게 T = Int 타입으로 선언됨
    
    mutating func push(item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
}

struct DoubleStack: Stack {
    typealias T = Double //typealias로 선언해줄 수도 있음
    
    var items: [T] = []
    
    mutating func push(item: T) {
        items.append(item)
    }
    mutating func pop() -> T {
        return items.removeLast()
    }
}

 

6. 제네릭 서브스크립트

  • 서브스크립트에도 제네릭을 활용할 수 있음
import UIKit

struct CustomArray<T> {
    private var items: [T] = []
    
    subscript<Indices: Sequence>(indices: Indices) -> [T] where Indices.Iterator.Element == Int {
        var result = [T]()
        for i in indices {
            result.append(self.items[i])
        }
        return result
    }
}
728x90

'Swift' 카테고리의 다른 글

[Swift] where절  (0) 2021.05.31
[Swift] inout 파라미터  (0) 2021.05.28
[Swift] 이니셜라이저 (Initializer)  (0) 2021.05.28
[Swift] 옵셔널 추출 (Optional Unwrapping)  (0) 2021.05.05
[Swift] Selector  (0) 2021.04.22

댓글