Сложность понимания связанных типов в расширениях протокола Swift

Я изо всех сил пытаюсь быстро понять протоколы и расширения протоколов.

Я хочу определить серию протоколов, которые могут быть применены к классу, а также набор расширений протокола для обеспечения реализаций по умолчанию. Пример кода:

// 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'. Если я удалю Self из Self.ResultType, я получаю ошибку '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

Если я совершенно не понимаю, как расширения протокола работают для обеспечения реализации по умолчанию, я открываю идеи о том, как достичь того же результата.


person So Over It    schedule 11.10.2015    source источник


Ответы (1)


Компилятор Swift определяет тип ResultType по сигнатурам типов реализованных методов протокола. Например, в следующем объявлении StringOutputItem компилятор знает, что ResultType StringOutputItem имеет тип 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'

Явно объявляя typealias ResultType = String в StringOutputItem, мы гарантируем, что при реализации методов протокола используется правильный тип.

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'
person blau    schedule 11.10.2015