조앤의 기술블로그

[Swift] Generics (제네릭) 본문

Study/Swift

[Swift] Generics (제네릭)

쬬앤 2020. 3. 9. 14:17

제네릭을 사용하면 함수를 작성하고, 어떠한 타입과도 동작할 수 있게 해줍니다.

코드의 중복을 피할 수 있기 때문에, 유연하고(flexible), 재사용성(reusable)이 높은 코드를 작성할 수 있습니다. 

 

제네릭은 스위프트의 가장 큰 특징 중 하나입니다. 그리고 스위프트의 많은 standard library가 제네릭으로 작성되어 있습니다. 

(Array, Dictionary...)

 

[제네릭으로 해결할 수 있는 문제점]

예를 들어 swap함수를 생각해보겠습니다. 

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
	let tmp = a
    a = b
    b = tmp
}

이 함수로는 Int 형의 두 수만 swap할 수 있습니다. 

 

String형, Double형의 값을 swap하려면 각각 자료형에 맞는 함수를 새로 생성해야 합니다. 

func swapTwoStrings(_ a: inout String, _ b: inout String) {
	let tmp = a
    a = b
    b = tmp
}

func swapTwoDouble(_ a: inout Double, _ b: inout Double) {
	let tmp = a
    a = b
    b = tmp
}

이는 함수 코드의 중복 문제점, 그리고 코드가 수정될 경우 모든 함수의 코드를 수정해야 한다는 문제점이 있습니다. 

제네릭을 이용하여 이 문제를 해결할 수 있습니다. 

 

[Generic Functions]

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
	let tmp = a
	a = b
	b = tmp
}

제네릭 함수는 어떤 타입과도 실행될 수 있습니다. 

var someInt = 93
var anotherInt = 309
swapTwoValues(&someInt, &anotherInt)

var someString = "Happy"
var anotherString = "Birthday"
swapTwoValues(&someString, &anotherString)

 

[Type Parameters]

T를 타입 파라미터라고 합니다. 

타입 파라미터 T는 실제 자료형으로 대체되는 placeholder입니다. 

 

 

 

[Generic Types]

스택 구조체를 만들어 예시를 보겠습니다.

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

이 구조체는 Int형만 스택에 push, pop할 수 있습니다. 

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

같은 코드로 <Element>타입 파라미터로 대체하여 만든 stack 구조체입니다.

Element 파라미터에 어떠한 형식이든 올 수 있습니다.

var stackOfStrings = Stack<String>()
stackOfStrings.push("하나")
stackOfStrings.push("둘")
stackOfStrings.push("셋")
stackOfStrings.push("넷")

let fromTheTop = stackOfStrings.pop()

 

[Associated Types]

프로토콜을 선언할 때 제네릭을 사용하려면 'associatedtype' 이라는 키워드를 사용합니다.

선언된 형식은 프로토콜 형식 내에서 실제 형식으로 대체되는 placeholder입니다. 

 

Container 프로토콜을 선언하겠습니다. 

protocol Container {
	associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

 

아까의 Stack 구조체에 Container프로토콜을 채용해보겠습니다. 

struct Stack<Element>: Container {
	//original Stack<Element> implementation
    var items = [Element]()
    mutating func push(_ item: Element) {
    	items.append(item)
    }
    mutating func pop() -> Element {
    	return items.removeLast()
    }
    
    //conformance to the Container Protocol
    //typealias Item = Element
    // 형식 추론으로 타입이 추론되기 때문에 생략해도 됩니다.
    mutaing func append(_ item: Element){
    	self.push(item)
    }
    var count: Int {
    	return items.count
    }
    subscript(i: Int) -> Element {
    	return items[i]
    }
}

associatedtype Item에 Element가 대입되었습니다

'Study > Swift' 카테고리의 다른 글

[Swift] Protocol (프로토콜) #1  (0) 2020.03.04
[Swift] Extension (익스텐션)  (0) 2020.02.27
[Swift] Initializers #2  (0) 2020.02.26
[Swift] Initializers #1  (0) 2020.02.26
[Swift] Type Casting (타입 캐스팅)  (0) 2020.02.25