associatedtype은 protocol 안에서 사용되는 기능입니다. 해당 기능을 사용하면 protocol 내에서 사용되는 타입을 하나로 제한하지 않고 하나의 프로토콜로 다양한 타입을 가지고 protocol을 구현하는 클래스를 만들 수 있습니다.
associatedtype이라고 선언을 하고 타입의 이름을 정의합니다. 일종의 제네릭이라고 생각하면 편합니다. 그리고 나서 해당 타입을 protocol 안에서 변수 혹은 메소드들을 정의할 때 사용하면 됩니다.
protocol SomeProtocol {
associatedtype SomeType
var someProperty: SomeType { get }
init(_ someProperty: SomeType)
func someMethod() -> SomeType
}
자 이제 위 protocol을 채택하는 class를 구현해봅시다. 해당 protocol은 someProperty와 someMethod의 리턴 값으로 특정한 타입을 정한 것이 아니라 associatedtype을 사용했습니다. 이 경우 신기하게도 아래와 같은 2가지 다른 타입을 사용하는 class를 구현할 수 있습니다.
SomeClass의 경우 property와 method의 리턴값이 Int입니다. 반면에 AnotherClass의 경우 property와 method의 리턴값이 String입니다. 마치 제네릭 사용한 것과 동일하게 특정한 하나의 타입을 정한 것이 아니기 때문에 Int로도 구현하고 String으로도 구현할 수 있는 것입니다.
class SomeClass: SomeProtocol {
let someProperty: Int
required init(_ someProperty: Int) {
self.someProperty = someProperty
}
func someMethod() -> Int {
return 0
}
}
class AnotherClass: SomeProtocol {
let someProperty: String
required init(_ someProperty: String) {
self.someProperty = someProperty
}
func someMethod() -> String {
return "string"
}
}
이번에는 associatedtype에 어떤 protocol을 준수하는 타입들만 올 수 있도록 제한을 두어봅시다. associatedtype은 Sequence 프로토콜을 채택한 타입만 올 수 있도록 하겠습니다. 이렇게 하면 위의 구현에서 구현한 동일한 코드에 아래와 같은 에러메시지가 발생합니다.
protocol SomeProtocol {
associatedtype SomeType: Sequence
var someProperty: SomeType { get }
init(_ someProperty: SomeType)
func someMethod() -> SomeType
}
String은 Sequence를 채택하는 한편 Int는 아닙니다. 따라서 associatedtype에 위와 같은 제한이 생긴 순간 더 이상 SomeClass는 SomeProtocol을 채택하는 class가 아닙니다.
associatedtype은 제네릭과 유사합니다. 실제로 구현되기 전까지는 구체적으로 어떤 타입인지는 알 수 없게 되는 것이죠. 이 방식을 통해서 2개의 혹은 그 이상의 별도의 protocol로 구현해야 하는 것을 하나의 protocol로 구현할 수 있게 됩니다.