Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
This playground demonstrates a potentially confusing gotcha when using protocols that have default method implementations along with class inheritance.
//: # Protocols and Inheritance
//: This playground demonstrates a gotcha in the use of protcols with default
//: method implementations along with class inheritance.
import UIKit
//: This protocol defines a single method that takes an `Int` and returns an `Int`.
protocol Doable {
func doSomething() -> Int
}
//: This extension provides a default implementation which simply returns 1.
extension Doable {
func doSomething() -> Int {
print("Default doSomething implementation called.")
return 1
}
}
//: The `Base` class conforms to Queryable, but does not implement the
//: `doSomething(value:)` method, relying on the default implementation.
class Base: Doable {
}
//: The `Sub` class inherits from `Base` and adds its own implementation of the
//: `doSomething(value:)` protocol function.
class Sub: Base {
/// This custom impelmentation returns the stored itemCount.
func doSomething() -> Int {
print("Subclass implementation of doSomething() called.")
return 5
}
}
//: Now, let's build a list of objects conforming to Queryable, including 1 `Base` instance.
var doList: [Doable] = []
doList.append(Base())
doList.append(Sub())
doList.append(Base())
doList.append(Base())
doList.append(Base())
//: You might expect this reduce call, which adds up the result of invoking `doSomething`
//: on each of the Queryable objects would return a result of 8.
let sum = doList.reduce(0, { (count, query) -> Int in
let itemCount = query.doSomething()
return count + itemCount
})
//: However, we get 5, and you can see in the console that the print statement in the
//: `Sub` implementation of `doSomething` is never called.
print("Final sum: \(sum)")
//: This is because `Sub` does not conform to `Queryable`, `Base` does. When the resolver
//: checks if `Sub` has an implementation of `doSomething` and it does not, the resolver
//: uses the default implementation instead.
//:
//: In order to make this work as expected:
//: * Add the `doSomething` method to `Base` (it can just return 1).
//: * Change the signature of `doSomething` in `Sub` to include the `override` keyword.
//:
//: ### Try it out!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment