제네릭 - 스위프트

Swift를 사용하면 특정 구체적인 유형에 연결되지 않은 일반 유형, 프로토콜 및 함수를 생성할 수 있지만 대신 주어진 요구 사항 집합을 충족하는 모든 유형과 함께 사용할 수 있습니다.

유형 안전성을 강력하게 강조하는 언어인 제네릭은 제네릭을 상당히 많이 사용하는 표준 라이브러리를 포함하여 Swift의 여러 측면에 핵심적인 필수 기능입니다. Array 및 Dictionary와 같은 기본 데이터 구조 중 일부를 살펴보십시오. 둘 다 제네릭입니다.

제네릭을 사용하면 동일한 유형, 프로토콜 또는 기능을 많은 사용 사례에 특화할 수 있습니다. 예를 들어 Array는 제네릭이므로 모든 종류의 유형에 대해 특수화된 인스턴스를 만들 수 있습니다.

var array = ["One", "Two", "Three"]
array.append("Four")

// This won't compile, since the above array is specialised
// for strings, meaning that other values can't be inserted:
array.append(5)

// As we pull an element out of the array, we can still treat
// it like a normal string, since we have full type safety.
let characterCount = array[0].count


우리 고유의 제네릭을 생성하려면 제네릭 유형이 무엇인지 정의하고 선택적으로 제약 조건을 첨부하기만 하면 됩니다. 예를 들어 여기에서는 날짜와 함께 모든 값을 포함할 수 있는 컨테이너 유형을 만듭니다.

struct Container<Value> {
    var value: Value
    var date: Date
}


특수 배열 및 사전을 생성할 수 있는 방법과 마찬가지로 문자열 또는 정수와 같은 모든 종류의 값에 대해 위의 컨테이너를 특수화할 수 있습니다.

let stringContainer = Container(value: "Message", date: Date())
let intContainer = Container(value: 7, date: Date())


위에서 Container를 특수화하는 구체적인 유형을 지정할 필요가 없다는 점에 유의하십시오. Swift의 유형 추론은 stringContainer가 Container 인스턴스이고 intContainer가 Container의 인스턴스라는 것을 자동으로 파악합니다.

제네릭 유형



이들은 Array 및 Dictionary와 유사한 방식으로 모든 유형에서 작동할 수 있는 사용자 지정 클래스, 구조 및 열거형입니다.

스택을 생성하자,

enum StackError: Error {
    case Empty(message: String)
}

public struct Stack<T> {
    var array: [T] = []

    init(capacity: Int) {
        array.reserveCapacity(capacity)
    }

    public mutating func push(element: T) {
        array.append(element)
    }

    public mutating func pop() -> T? {
        return array.popLast()
    }

    public func peek() throws -> T {
        guard !isEmpty(), let lastElement = array.last else {
            throw StackError.Empty(message: "Array is empty")
        }
        return lastElement
    }

    func isEmpty() -> Bool {
        return array.isEmpty
    }
}

extension Stack: CustomStringConvertible {
    public var description: String {
        let elements = array.map{ "\($0)" }.joined(separator: "\n")
        return elements
    }
}

var stack = Stack<Int>(capacity: 10)
stack.push(element: 1)
stack.push(element: 2)
print(stack)

var strigStack = Stack<String>(capacity: 10)
strigStack.push(element: "aaina")
print(strigStack)


제네릭 형식 제약



제네릭은 모든 유형이 될 수 있으므로 많은 작업을 수행할 수 없습니다. 제네릭 함수 및 제네릭 형식과 함께 사용할 수 있는 형식에 형식 제약을 적용하는 것이 때때로 유용합니다. 유형 제약 조건은 유형 매개변수가 특정 프로토콜 또는 프로토콜 구성을 준수해야 함을 지정합니다.

For example, Swift’s Dictionary type places a limitation on the types that can be used as keys for a dictionary. Dictionary needs its keys to be hashable so that it can check whether it already contains a value for a particular key.



func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
       // function body goes here
}


관련 유형



연관된 유형은 프로토콜의 일부로 사용되는 유형에 자리 표시자 이름을 제공합니다. 관련 유형에 사용할 실제 유형은 프로토콜이 채택될 때까지 지정되지 않습니다.

연결된 유형에 제약 조건 추가



준수 유형이 해당 제약 조건을 충족하도록 요구하기 위해 프로토콜의 관련 유형에 유형 제약 조건을 추가할 수 있습니다.

protocol Stackable {
    associatedtype Element: Equatable
    mutating func push(element: Element)
    mutating func pop() -> Element?
    func peek() throws -> Element
    func isEmpty() -> Bool
    func count() -> Int
}


이제 스택의 요소 유형은 Equatable을 준수해야 합니다. 그렇지 않으면 컴파일 시간 오류가 발생합니다.

재귀 프로토콜 제약:



프로토콜은 자체 요구 사항의 일부로 나타날 수 있습니다.

protocol SuffixableContainer: Container {
    associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
    func suffix(_ size: Int) -> Suffix
}


Suffix에는 두 가지 제약이 있습니다. SuffixableContainer 프로토콜(현재 정의 중인 프로토콜)을 준수해야 하고 항목 유형이 컨테이너의 항목 유형과 동일해야 합니다.

제네릭 유형 확장:



제네릭 유형을 확장할 때 확장 정의의 일부로 유형 매개변수 목록을 제공하지 않습니다. 대신 원래 유형 정의의 유형 매개변수 목록은 확장 본문 내에서 사용할 수 있으며 원래 유형 매개변수 이름은 원래 정의의 유형 매개변수를 참조하는 데 사용됩니다.

extension Stack {
   var topItem: Element? {
     return items.isEmpty ? nil : items[items.count - 1]
   }
}


일반 Where 절이 있는 확장



확장의 일부로 일반 where 절을 사용할 수도 있습니다. 아래 예제는 이전 예제에서 일반 스택 구조를 확장하여 isTop(_:) 메서드를 추가합니다.

extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        guard let topItem = items.last else {
            return false
        }
        return topItem == item
    }
}


확장은 스택의 항목이 동등할 때만 isTop(_:) 메서드를 추가합니다. 프로토콜 확장과 함께 일반 where 절을 사용할 수도 있습니다. 쉼표로 구분하여 where 절에 여러 요구 사항을 추가할 수 있습니다.

일반 전문화:



일반 특수화는 컴파일러가 Int와 같은 구체적인 매개변수 유형에 대해 Stack과 같은 일반 유형 또는 함수를 복제함을 의미합니다. 그런 다음 이 특수 함수를 Int에 대해 특별히 최적화하여 모든 간접 참조를 제거할 수 있습니다. 컴파일 타임에 형식 매개 변수를 형식 인수로 바꾸는 프로세스를 특수화라고 합니다.

좋은 웹페이지 즐겨찾기