繁体   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