I recently run into a problem. To simplify it, this is how it looks like:
// Class A is a class from an external framework
// and its implementation cannot be changed
class A { }
protocol HanderProtocol {
static var shouldHandle: Bool { get }
}
class Hander1: HanderProtocol {
static let shouldHandle = true
}
class Hander2: HanderProtocol {
static let shouldHandle = false
}
protocol Handleable {
associatedtype Handler: HanderProtocol
}
extension Handleable {
var shouldHandle: Bool {
Self.Handler.shouldHandle
}
}
extension A: Handleable {
typealias Handler = Hander1
}
class B: A {
typealias Handler = Hander2
}
print(A().shouldHandle) // prints 'true'
print(B().shouldHandle) // prints 'true' as well
class B's associatedtype Handler is class Hander2, which always return false.
This comes to me as a surprise. Is this expected? From my perspective, this is not, but if it is expected, shouldn't we forbid overriding the associatedtype?
protocol P { associatedtype A }
class C: P { typealias A = Int }
class D: C { typealias A = String }
extension P {
static func printA() { print(A.self) }
}
D.printA() // Int
print(D.A.self) // String
This behavior occurs because D’s conformance to P is inherited from C.
The only way in which D can customize the conformance, is by overriding methods from C that satisfy requirements of P. Since a typealias cannot be overridden, it follows that D cannot provide a different one for its conformance to P.
Note that the typealias in Ddoes work when using D directly. It is just not the one used for purposes of conformance to P, since that conformance comes from C.
• • •
There are a number of other complications surrounding class and subclass conformances to protocols, especially when generics and/or existentials are involved:
SR–103 describes the inability of a subclass to override a protocol requirement for which the superclass uses a default implementation from the protocol.
The thread Need help understanding protocols and generics covers some strange edge-cases. This post by @xwu lists just about every possible combination you can imagine, and explains the behavior in all but the last one, which was discovered by @Lantua a few posts earlier.