Generics

Eli·2021년 2월 7일
1

Swift

목록 보기
15/17
post-thumbnail

The Problem That Generics Solve

//아래와 같이 같은 기능을 구현하고 있지만 타입이 달라 3개의 메소드를 각각 정의해줘야 했다.
//제네릭은 아래와 같은 문제들을 해결해, 재사용 가능한 코드들 작성하게 한다.
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

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

func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
    let temporaryA = a
    a = b
    b = temporaryA
}

Generic Functions

플레이스 홀더인 임의의 타입을 선언해 사용가능

//T라는 플레이스홀더를 생성해 사용하는 함수
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

//사용
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt is now 107, and anotherInt is now 3

var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello"

Naming Type Parameters

element간에 상관관계가 있는 경우 이름 파라미터

아닐경우 T, U, V 같은 대문자 파라미터를 관행적으로 사용

Generic Types

함수외에서 class나 struct 등의 타입에서도 사용 가능

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

Type Constraints

지정하는 제네릭 타입이 특정 클래스를 상속 또는 프로토콜을 따르도록 제한하는 기능

//T는 SomeClass를 상속해야하며,
//U는 SomeProtocol을 따라야만 사용할 수 있다.
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
}

Associated Types

프로토콜에서 타입에 플레이스홀더를 부여하여 사용할 수 있는 기능

protocol Container {
		//아래와같이 지정하여 프로토콜 내부에서 타입으로 사용가능
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

//사용예시: Int로 사용
struct IntStack: Container {
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
    typealias Item = Int
    mutating func append(_ item: Int) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}

//사용예시: 제네릭 타입을 사용
struct SomeContainer<Some>: Container {
    var items: [Some] = []
    mutating func append(_ item: Some) {
        items.append(item)
    }
    
    var count: Int = 0
    
    subscript(i: Int) -> Some {
        return items[i]
    }
    
    typealias Item = Some
}

//Associated Types에 조건걸기
//where 구문을 이용해 Suffix의 Item과 Item이 동일해야한다는 조건정의
protocol SuffixableContainer: Container {
    associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
    func suffix(_ size: Int) -> Suffix
}

Generic Where Clauses

where 절의 경우에 가끔가다 라이브러리 코드에서 보긴 했는데, 아직 구현이 자유자제로 필요할 때 쓰지 못하는 기능인 것 같다.

  1. 함수에서 Where

    C1과 C2 타입이 같고 C1이 Equatable 일 경우 사용 가능

func allItemsMatch<C1: Container, C2: Container>
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.Item == C2.Item, C1.Item: Equatable {
        if someContainer.count != anotherContainer.count {
            return false
        }
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }

        return true
}
  1. Extension에서 Where
extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        guard let topItem = items.last else {
            return false
        }
        return topItem == item
    }
}
  1. protocol의 상속에서 Where
protocol ComparableContainer: Container where Item: Comparable { }

#학습에 대한 내용으로 틀린 내용이 있을 수 있습니다.
#댓글로 남겨주시면 더 좋은 게시글로 수정하도록 하겠습니다.

profile
애플을 좋아한다. 그래서 iOS 개발을 한다. @Kurly

0개의 댓글