Swift - upcasting array of protocol to array of super protocol causes error

前端 未结 2 1996
有刺的猬
有刺的猬 2021-01-20 14:54

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

相关标签:
2条回答
  • 2021-01-20 15:21

    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.

    Workaround

    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.

    0 讨论(0)
  • 2021-01-20 15:34

    Try this code - just checked, works fine

    let array2: [SuperProtocol] = array1.map { $0 as SuperProtocol }
    
    0 讨论(0)
提交回复
热议问题