[SWIFT] Generic

2020. 8. 22. 01:08SWIFT

반응형

SWIFT 언어의 강력한 기능 중 하나로 알고 있는 Generic에 대해 알아볼까해요~~

 

Generic 타입을 사용하면 코드를 깔끔하게 사용할 수 있고 무엇보다 코드의 중복을 줄일 수 있어요.

추상적인 표현으로 사용이 가능하기 때문에 더욱 추상적으로 코드의 중복을 줄일 수 있다고 이해하면 좋을 것 같아요.

 

잘 생각해보면 그 동안 자주 사용했던 Array, Dictionary 타입도 Generic 컬렉션이에요.

Array, Dictionary에는 여러 타입들이 들어갈 수 있죠⁉️

 

모두 Generic으로 구현했기 때문이라고 알면 될 것 같습니다.

 

그렇다면, 예시를 보고 어떤 식으로 사용하는지 알아볼까요?

애플에서 제공하는 예시입니다.

 

Generic 함수

// 두 Int 값을 교환하는 메소드

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

// 두 String 값을 교환하는 메소드
func swapToStrings(_ a: inout String, _ b; inout: String) {
    let tamporaryA: String = a
    a = b
    b = temporaryA   
}

 

어떤가요? 

다른 타입의 사용을 위해 단순 교환을 위해 두 개의 메소드를 생성했어요...

뭔가 하나로 합칠 수 있을 것 같지 않나요⁉️

 

여기서 Generic을 사용하면 코드가 간단해집니다‼️

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

 

바뀐 점이 보이시나요⁉️

 

두 개의 메소드를 제네릭을 사용해서 간단하게 표현했죠.

이 함수 하나를 이용해서 Int, String, Double, Float 의 교환이 모두 가능해졌죠.

확실히 코드가 줄고 일반적이 되었죠⁉️

 

모양새가 조금 달라졌는데, 바로 T라는 처음보는 타입이 추가되었죠?

이것을 플레이스홀더라고 합니다.

 

메소드를 읽어보면 어떤 T라는 플레이스홀더 타입 두 가지를 교환한다.

그리고 두 변수는 같은 타입이다라는 것을 알 수 있겠죠.

 

이 플레이스홀더 타입은 메소드가 호출되는 순간에 결정됩니다.

 

또한 이 플레이스홀더에는 T 이외의 이름도 설정이 가능합니다.

보통 V, T, U와 같이 알파벳을 사용하거나 <V, T, U>와 같이 여러가지도 설정이 가능합니다.

 

<Element>와 같이 자유롭게도 설정이 가능합니다.

 

Generic 타입

Generic으로 함수를 사용할 경우 위와 같이 사용하였습니다.

이번에는 Generic의 타입을 구현하여 이 타입을 대상으로 어떻게든 연관되어 사용이 가능한 방법을 알아보겠습니다.

 

우선 Generic을 사용하지 않은 Stack 타입입니다.

// Generic 사용하지 않은 경우
struct IntStack() {
    var items = [Int]()

    mutating func push(_ item: Int) {
    	items.append(item)
    }
    
    mutating func pop() -> Int {
    	return items.removeLast()
    }
}

 

이렇게 사용을 하게 되면, Int 타입으로만 한정되는 것이 보이시죠⁉️

 

위에서 보았던대로 Generic을 이용해서 구현을 해볼까요.

// Generic 타입을 사용한 경우
struct Stack<Element> {
    var items = [Element]()
    
    mutating func push(_ item: Element) {
    	items.append(item)
    }
    
    mutating func pop() -> Element {
    	return items.removeLast()
    }
}

 

이렇게 구현을 하게 되면 모든 타입을 대상으로 동작할 수 있는 Stack 구조체가 만들어졌습니다‼️

 

단, 여기서 꼭 명심하셔야 할 것이 모든 타입이 섞여서 들어간다는 것은 아닙니다.

 

이 플레이스홀더 타입은 스택이 호출되는 순간에 결정됩니다.

 

즉, 초기에 들어갔던 값을 기준으로 타입이 결정되어집니다.

 

 

Generic 타입 제약

Generic을 사용할 때, 플레이스홀더로 타입을 결정했던 것 기억하시나요⁉️

 

이전에는 이 플레이스홀더에 어떤 타입이든 들어올 수 있었죠.

하지만 이 플레이스홀더에 들어오는 타입에 제약을 줄 수도 있답니다.

 

여기서 주의해야할 점은 타입 제약은 클래스 타입 또는 프로토콜로만 가능합니다.

// Generic 타입 제약
func swapToValues<T: BinaryInteger>(_ a: inout T, _ b: inout T) {
}

struct Stack<Element: Hashable> {
}

 

다음과 같이 구현하게 되면 각각은 세미콜론 : 뒤에 있는 프로토콜을 준수할 경우에만 사용할 수 있습니다.

 

 

오늘은 Generic에 대해 알아보았는데요~~

Generic을 잘만 사용하면 코드를 좀 더 짧고 깔끔하게 짤 수 있을 것 같죠⁉️

 

 

반응형