I have a protocol with PAT that provides a default implementation in an extension. Then, a class conforming that protocol provides an "override". This override is not called, and the compiler "prefers" the default.
//---- Definition of the protocol with a default implentation, because I am forced to.
protocol Bag: AnyObject {
associatedtype BagObject
func add(_ e: BagObject)
}
extension Bag where BagObject: Equatable {
// I here give a default implementation, because I can't do differently, as this
// is an extension, and it is an extension because it only applies to assoicated
// types that are Equatables
func contains(_ e: BagObject) -> Bool {
print("Default implementation is called")
return false
}
}
///---- Definition of a class that gives a concrete implementation of the protocol
class BagConcreteImplementation<BagObject>: Bag {
var bag = Array<BagObject>()
func add(_ e: BagObject) { bag.append(e) }
}
extension BagConcreteImplementation where BagObject: Equatable {
// I here give a concrete implementation when the generic type is Equatable
func contains(_ e: BagObject) -> Bool {
print("Concrete implementation is called")
return bag.contains(e)
}
}
///---- This is a class that encapsulate a bag, in real life, this class is adding some filtering to objects that can be added to the bag
class AClassThatHaveABag<BagType: Bag> {
typealias BagObject = BagType.BagObject
let myBag: BagType
init(bag: BagType) { myBag = bag }
}
extension AClassThatHaveABag where BagType.BagObject: Equatable {
func contains(_ e: BagObject) {
// The problem here is that the compiler only knows that myBag is a Bag
// of Int and therefore calls the default implementation
// It does not call the concrete implementation of the concrete class that WILL be provided
myBag.contains(e)
}
}
let aBagOfInt = BagConcreteImplementation<Int>()
let objectThatContainsABagOfInt = AClassThatHaveABag(bag: aBagOfInt)
aBagOfInt.contains(0)
objectThatContainsABagOfInt.contains(0)
// Prints
// Concrete implementation is called
// Default implementation is called
We can clearly see the direct call is call the concrete implementation, while the encapsulated call is calling the default implementation.
How to do to make sure the concrete implementation is always called, even through an encapsulation ?