[英]How to force usage of protocol default extension "override"?
我有一個帶有 PAT 的協議,它在擴展中提供了默認實現。 然后,符合該協議的 class 提供“覆蓋”。 不調用此覆蓋,編譯器“首選”默認值。
//---- 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
我們可以清楚地看到直接調用是調用具體實現,而封裝調用是調用默認實現。
即使通過封裝,如何確保始終調用具體實現?
我嘗試了一些事情,不知何故這有效:
在協議中聲明contains
,而不是在擴展中,如下所示:
func contains<T>(_ e: T) -> Bool where T: Equatable, T == BagObject
如果您在 class 中執行T == BagObject
類的操作,則會出現編譯器錯誤,指出這會使T
變得多余,但顯然這在協議中是可以的。
然后,像這樣實現默認實現:
extension Bag {
func contains<T>(_ e: T) -> Bool where T: Equatable, T == BagObject {
print("Default implementation is called")
return false
}
}
然后,您可以直接在 class 中實現具體實現,作為非泛型方法。
class BagConcreteImplementation<BagObject> : Bag {
func contains(_ e: BagObject) -> Bool where BagObject : Equatable {
print("Concrete implementation is called")
return bag.contains(e)
}
var bag = Array<BagObject>()
func add(_ e: BagObject) { bag.append(e) }
}
在不更改調用站點的情況下,代碼將打印:
Concrete implementation is called
Concrete implementation is called
為了完整起見,我在這里發布了另一個解決方案,它避免使用我發現(我的拙見)不那么干凈的T == BagObject
。
這個想法是使用Bag
的專用版本,稱為BagOfEquatables
,並擴展類
protocol Bag: AnyObject {
associatedtype BagObject
func add(_ e: BagObject)
}
// Specialized version of Bag for Equatables
protocol BagOfEquatables: Bag where BagObject: Equatable{
func contains(_ e: BagObject) -> Bool
}
extension BagOfEquatables {
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: BagOfEquatables where BagObject: Equatable {
func contains(_ e: BagObject) -> Bool {
print("Concrete implementation is called")
return bag.contains(e)
}
}
///---- This is a class that encapsulate a bag
class AClassThatHaveABag<BagType: Bag> {
typealias BagObject = BagType.BagObject
let myBag: BagType
init(bag: BagType) { myBag = bag }
}
extension AClassThatHaveABag where BagType: BagOfEquatables {
func contains(_ e: BagObject) -> Bool {
myBag.contains(e)
}
}
let aBagOfInt = BagConcreteImplementation<Int>()
let objectThatContainsABagOfInt = AClassThatHaveABag(bag: aBagOfInt)
aBagOfInt.contains(0)
objectThatContainsABagOfInt.contains(0)
// Prints
// Concrete implementation is called
// Concrete implementation is called
你說:
不調用此覆蓋,編譯器“首選”默認值。
問題是您在擴展中提供了該方法的默認實現,但從未將其聲明為協議本身的一部分。 結果,編譯器將使用“靜態調度”。
如果您在協議中包含該方法,編譯器將使用“動態調度”來解析要調用的方法。
例如:
protocol Foo {
// without this declaration, this will use static dispatch;
// uncomment the following line for dynamic dispatch
// func foo()
}
extension Foo {
func foo() {
print("default")
}
}
struct Bar: Foo {
func foo() {
print("bar implementation")
}
}
let bar: Foo = Bar()
bar.foo() // “default” with static dispatch; if you uncomment the line above for dynamic dispatch, it will change to ”bar implementation“
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.