In Swift, why subclass method cannot override the

2020-03-03 06:04发布

问题:

I know the title of this question is confusing but the weird behaviour is explained in the example below:

protocol Protocol {
    func method() -> String
}

extension Protocol {
    func method() -> String {
        return "From Base"
    }
}

class SuperClass: Protocol {
}

class SubClass: SuperClass {
    func method() -> String {
        return "From Class2"
    }
}

let c1: Protocol = SuperClass()
c1.method() // "From Base"
let c2: Protocol = SubClass()
c2.method() // "From Base"

How come c1.method() and c2.method() return the same? How come the method() in SubClass doesn't work?

Interestingly, without declaring the type of c2, this is going to work:

let c2  = SubClass()
c2.method() // "From Class2"

回答1:

The problem is that c1 and c2 are of type Protocol, as you've defined their type explicitly this way (remember: protocols are fully fledged types). This means, when calling method(), Swift calls Protocol.method.


If you define something like:

let c3 = SuperClass()

...c3 is of type SuperClass. As SuperClass has no more specific method() declaration, Protocol.method() is still used, when calling c3.method().


If you define something like:

let c4 = SubClass()

...c4 is of type SubClass. As SubClass does have a more specific method() declaration, SubClass.method() is used, when calling c4.method().


You could also get c2 to call SubClass.method(), by down-casting it to `SubClass:

(c2 as! SubClass).method() // returns "From Class2"

Here's a demonstration on SwiftStub.



回答2:

I'm not quite sure of the underlying mechanism but it must be something to do with the fact that protocols don't necessarily allow for inheritance.

One way to work around this problem is by also adding the method to SuperClass

import Foundation
protocol Protocol: class {
    func method() -> String
}

extension Protocol {
    func method() -> String {
        return "From Base"
    }
}

class SuperClass: Protocol {
    func method() -> String {
        return "From Super"
        }
}

class SubClass: SuperClass {
    override func method() -> String {
        return "From Class2"
    }
}

let c1: Protocol = SuperClass()
c1.method() // "From Super"
let c2: Protocol = SubClass()
c2.method() // "From Class2"


回答3:

Basically yes, if there is a superclass that conforms to the protocol but doesn't provide an implementation for it, the protocol extension's implementation will be available in subclasses, even if the sublcasses have implementation (however they have to be casted to the superclass's or to the protocol's type).

However, if the superclass has an implementation of the protocol's method, then protocol's implementation won't be available from neither the superclass nor it's subclasses.