[Swift] 제네릭(Generic) 2편 - 타입, 프로토콜

어흥·2024년 6월 17일

Swift

목록 보기
23/28

제네릭 타입 - 구조체, 클래스, 열거형

제네릭 함수처럼 Swift에서는 generic type을 정의할 수 있음. 어떠한 타입에서 사용할 수 있는 커스텀 클래스, 구조체, 열거형을 만들 수 있음 ArrayDictionary 도 제네릭으로 구현

타입에 상관없이 사용가능한 Stack<Element> 구조체

  • 제네릭 함수와 동일하게 타입 이름 뒤에 ‘<>’를 작성하고 안에 타입 파라미터를 작성
struct Stack<Element> {
    var items: [Element] = []
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

제네릭 열거형

열거형에서 연관값에 대해서 제네릭 코드를 작성할 수 있음

enum Color<T> {
	case red (T)
	case green
	case blue
}

확장

확장의 경우, 타입 파라미터를 명시하지 않아도 됨 → 본체에서 정의한 타입 파라미터 적용

extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}
  • where 절을 활용하여 확장을 적용할 타입 제한 가능
extension Stack where Element == Int {
    var squaredTopItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1] * items[items.count - 1]
    }
}

var iStack = Stack(items: [1, 2, 3])
iStack.squaredTopItem // 9

타입 제약

파라미터 타입을 특정 프로토콜을 채택하는 타입 혹은 단일 클래스에 해당하는 타입에 대해서 제약 가능

  • 작성 방법 타입 파라미터 뒤에 ‘:’ 작성 후, 프로토콜 혹은 클래스 이름 작성
  1. 프로토콜 제약

Equatable 프로토콜을 채택한 타입에 대해서만 Stack 사용 가능

struct Stack<Element : Equatable> {
    var items: [Element] = []
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}
  1. 클래스 제약

    특정 클래스와 상속관계에 내에 있는 클래스만 타입으로 사용할 수 있는 제약

    • 구조체, 열거형 사용 못 함
class Person {}
class Student: Person {}

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

제네릭 함수, 타입이 구체적으로 명시된 함수 중 어느 것이 실행될까?

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
    print("swap 완료")
}

func swapTwoValues(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
    print("swap 완료 및 원소 더한 값:", a + b)
}
var a = 2
var b  = 3
swapTwoValues(&a, &b)

구체적으로 타입이 명시된 함수가 실행된다!

프로토콜 제네릭 사용

프로토콜을 제네릭 선언 방법

  • associatedtype 뒤에 타입 파라미터를 명시
  • ===> associatedtype T
protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

당연히 해당 타입에 대한 구체적인 명시는 프로토콜을 채택한 타입에서 작성

  • typealias T = Int 작성하여 실제 타입 표시 but 연관 타입이 추론이 가능하므로 생략 가능
struct TV: RemoteControl {   
    typealias T = Int       // 생략 가능
    
    func changeChannel(to: Int) {
        print("TV 채널바꿈: \(to)")
    }
    
    func alert() -> Int? {
        return 1
    }
}

물론 연관 타입에 대해서 제약을 걸 수 있음

protocol Container {
    associatedtype Item: Equatable
}

Reference.

Generics | Documentation
앨런 Swift문법 마스터 스쿨

0개의 댓글