제너릭(제너릭 타입)은 유연하고 재사용 가능한 함수 및 타입을 정의할 수 있게 해줍니다. Swift의 주요 특징은 type safety을 유지하는 것으로, 컴파일러가 컴파일 중에 특정 타입과의 작업을 검증합니다. 제너릭 타입은 where 조건, 프로토콜 상속 및 이들의 조합을 통해 제한할 수 있습니다.
코드 예제:
func swapValues<T>(_ a: inout T, _ b: inout T) { let temp = a a = b b = temp } protocol Drawable { func draw() } func drawAll<T: Drawable>(_ items: [T]) { for item in items { item.draw() } } // 프로토콜과 where 조건에 의한 제한 func compareValues<T: Equatable>(_ a: T, _ b: T) -> Bool { return a == b }
제너릭 함수가 오버로드된 함수 역할을 할 수 있습니까? 컴파일러는 필요한 구현을 어떻게 선택합니까?
답변: 네, 제너릭 함수는 일반 함수 및 다른 제너릭 함수와 함께 오버로드될 수 있습니다. 컴파일러는 가장 구체적인 구현을 선택하려고 합니다. 예:
func printValue(_ value: Int) { print("Int: \(value)") } func printValue<T>(_ value: T) { print("Generic: \(value)") } printValue(5) // Int: 5 printValue("Swift") // Generic: Swift
이야기
팀은 인덱스를 찾기 위해 배열의 제너릭 확장을 작성했으나, Equatable을 통해 타입을 제한하는 것을 잊었습니다. 이로 인해 Equatable이 아닌 요소가 있는 배열에서 확장을 적용하려 할 때 컴파일 오류가 발생했습니다.
이야기
프로젝트에서 타입 제한 없이 제너릭 객체의 캐시를 구현하려고 했습니다. 그 결과, 런타임에서 다운캐스트 할 때 크래시가 발생했습니다 — 실제 안전한 사용은 associated type과 제약 조건을 가진 프로토콜을 통해 달성될 수 있었습니다.
이야기
개발자들이 제너릭 클래스를 구현했지만, 상속 및 오버라이드 시 자식의 전체 제너릭 매개변수를 지정해야 하는 것을 잊었습니다. 이로 인해 코드는 컴파일되지 않았고 타입 계층을 완전히 재구성해야 했습니다.