← 아카이브로 돌아가기

Development · Swift

Swift 제네릭으로 재사용 가능한 코드 만들기

제네릭 함수와 타입, 타입 제약을 이용해 타입 안전성을 유지하면서 중복을 줄이는 방법을 정리합니다.

Velog 원문 보기 ↗

Development 아카이브 표지

타입만 다르고 하는 일은 같은 함수를 여러 번 만들기 시작하면 구현보다 중복이 먼저 눈에 들어온다. 제네릭은 구체적인 타입을 잠시 미뤄 두고 공통 동작을 한 번만 작성하게 해준다.

Generic Fuction

함수를 선언할 때 제네릭을 사용하여 타입에 의존하지 않는 범용 코드를 작성할 수 있다. <T>와 같은 형태의 타입 매개변수(type parameter)를 사용하여 임의의 타입 T를 정의할 수 있다.

var a = 2
var b = 3
var apple = "apple"
var banana = "banana"

func swapElements<T>(_ first: inout T, _ second: inout T) {
    let temp = first
    first = second
    second = temp
}

swapElements(&a, &b) // Int 타입
swapElements(&apple, &banana) // String 타입

Comma(,)를 통해 여러 개의 타입을 선언할 수도 있다.

func someFunction<One, Two>(_ first: One, _ second: Two) { ... }

Generic Type

함수 이외에도 구조체, 클래스, 열거형 타입에 대한 제네릭 타입을 선언할 수 있다.

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

제너릭 타입에 대한 인스턴스를 생성할 때 어떤 타입으로 사용할 것인지 명시해야 한다.

let stack1: Stack<Int> = .init() // Int 타입에 대한 인스턴스
let stack2 = Stack<String>.init() // String 타입에 대한 인스턴스

Extending a Generic Type

익스텐션을 이용해 제네릭 타입을 확장할 수 있는데, 이때 기존에 선언한 타입 파라미터를 사용해야 한다.

extension Array {
    mutating func pop() -> Element {
        return self.removeLast()
    }
}

Type Constraints

제네릭에서 특정 클래스를 상속하거나 특정 프로토콜을 따르도록 제한을 둘 수 있다.

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { ... }
← 아카이브로 돌아가기