In Swift, I notice that I can upcast an object that conforms to a protocol called, let\'s say SubProtocol
to another protocol called SuperProtocol
The reason has to do with how protocols inherit differently from classes.
Consider first that protocols can have default implementations, for example:
protocol MammalLocomotion {
func legs() -> Int
}
extension MammalLocomotion {
func legs () -> Int {
return 2
}
}
protocol CowLocomotion : MammalLocomotion {
}
extension CowLocomotion {
func legs () -> Int {
return 4
}
}
Let's make classes that conform to these protocols:
class Mammal : MammalLocomotion {
}
class Cow : Mammal, CowLocomotion {
}
let mammal = Mammal()
let cow = Cow()
Their legs()
methods respond as we'd expect:
mammal.legs() // 2
cow.legs() // 4
But now let's cast cow
to Mammal
:
let cowAsMammal : Mammal = cow
cowAsMammal.legs() // 2
cow
had 4 legs, but now it has 2
. This is because, with protocols, the currently known type determines which default implementation is used. So casting the array doesn't work — I think the reasoning is that it would be unexpected for an array cast to alter its contained objects' behavior.
As you've noted, this won't work:
let farm : [CowLocomotion] = [Cow(), Cow(), Cow()]
let mammalFarm : [MammalLocomotion] = farm // doesn't work
If you want, you can work around this limitation by mapping the array to the protocol you want:
let farm = [Cow(), Cow(), Cow()]
farm.forEach { print($0.legs()) } // prints 4, 4, 4
let mammalFarm = farm.map { $0 as MammalLocomotion }
mammalFarm.forEach { print($0.legs()) } // prints 2, 2, 2
More information on how protocols inherit is available in the Protocol-Oriented Programming in Swift session from this year's WWDC - transcript here.
Try this code - just checked, works fine
let array2: [SuperProtocol] = array1.map { $0 as SuperProtocol }