簡體   English   中英

難以理解Swift協議擴展中的“關聯類型”

[英]Difficulty understanding `Associated Types` in Swift protocol extensions

我正在努力快速地了解協議和協議擴展。

我想定義一系列可應用於類的協議,以及一組協議擴展以提供默認的實現。 示例代碼:

// MARK: - Protocols & Protocol Extensions
protocol OutputItem {
    typealias ResultType
    func rawValue() -> ResultType
    // other requirements ...
}

protocol StringOutputItem : OutputItem {}
extension StringOutputItem {
    typealias ResultType = String
    override func rawValue() -> Self.ResultType {
        return "string ouput"
    }
}

protocol IntOutputItem: OutputItem {}
extension IntOutputItem {
    typealias ResultType = Int
    override func rawValue() -> Self.ResultType {
        return 123
    }
}

擴展中rawValue()的上述重寫函數Ambiguous type name 'ResultType' in 'Self'給出了錯誤的Ambiguous type name 'ResultType' in 'Self' 如果我從Self.ResultType刪除Self'ResultType' is ambiguous for type lookup in this context ,我將得到錯誤'ResultType' is ambiguous for type lookup in this context

我如何向協議擴展發出信號,以將哪種類型用於ResultType

我的目標是能夠將協議及其擴展應用於以下類:

// MARK: - Base Class
class DataItem {
    // Some base class methods
    func randomMethod() -> String {
        return "some random base class method"
    }
}

// MARK: - Subclasses
class StringItem : DataItem, StringOutputItem {
    // Some subclass methods
}

class AnotherStringItem : DataItem, StringOutputItem {
    // Some subclass methods
}

class IntItem : DataItem, IntOutputItem {
    // Some subclass methods
}

以便:

let item1 = StringItem()
print(item1.rawValue())         // should give "string output"

let item2 = AnotherStringItem()
print(item2.rawValue())         // should give "string output"

let item3 = IntItem()
print(item3.rawValue())         // should give 123

如果我對協議擴展如何提供默認實現完全不滿意,那么我對如何實現相同結果持開放態度。

Swift編譯器通過已實現的協議方法的類型簽名來推斷ResultType的類型。 例如,在以下StringOutputItem聲明中,編譯器知道StringOutputItemResultType的類型為String ,即使沒有顯式聲明也是如此:

protocol StringOutputItem: OutputItem {}

extension StringOutputItem {
    func rawValue() -> String {
        return "string output"
    }
}

class StringItem : DataItem, StringOutputItem {}

let item = StringItem()
print(item.rawValue()) // prints "string output"

我們可以在ResultType中顯式聲明StringOutputItem ,這將僅確保StringOutputItem符合OutputItem協議並以正確的類型實現它。

為了說明關聯類型的類型推斷,假設OutputItem指定另一種方法作為其協議的一部分。 如果我們提供默認的實現,其中類型不匹配,則編譯器將拋出錯誤,指示實現的類型不符合協議。

protocol OutputItem {
    typealias ResultType
    func rawValue() -> ResultType
    func printValue(r: ResultType)
}

protocol StringOutputItem: OutputItem {}

extension StringOutputItem {
    func rawValue() -> String {
        return "string output"
    }
    func printValue(r: Int) {  // Should be String
        ...
    }
}

struct Test: StringOutputItem {} // Error: Type 'Test' does not conform to protocol 'OutputItem'

通過在StringOutputItem顯式聲明typealias ResultType = String ,我們可以確保在實現協議的方法時使用正確的類型。

protocol OutputItem {
    typealias ResultType
    func rawValue() -> ResultType
    func printValue(r: ResultType)
}

protocol StringOutputItem: OutputItem {}

extension StringOutputItem {
    typealias ResultType = String // without this typealias declaration, the program WILL compile since ResultType is inferred to be of type Int
    func rawValue() -> Int {
        return 123
    }
    func printValue(r: Int) {  
        ...
    }
}

struct Test: StringOutputItem {} // Error: Type 'Test' does not conform to protocol 'OutputItem'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM