符合返回PAT的通用协议方法

时间:2018-12-03 21:03:33

标签: swift generics swift-protocols pats

我想声明类似于以下内容的通用协议:

protocol Factory {
    func createWidget<T, TWidget>(_ t: T) -> TWidget 
    where TWidget: Widget, TWidget.T == T
}

protocol Widget {
    associatedtype T
    func get() -> T
}

我希望我可以实现Factory的具体变体,并通过隐藏的实现返回它们自己的具体和不透明的Widget

这是无法构建的示例实现:

struct ConcreteFactory: Factory {
    func createWidget<T, TWidget>(_ t: T) -> TWidget 
    where TWidget: Widget, TWidget.T == T {
        // This line has an error…
        return ConcreteWidget(widgetValue: t)
    }
}

struct ConcreteWidget<T>: Widget {
    let widgetValue: T

    init(widgetValue: T) {
        self.widgetValue = widgetValue
    }

    func get() -> T {
        return widgetValue
    }
}

但是,这不能编译。

在指示的行,Swift的编译器给出错误“无法将类型'ConcreteWidget'的返回表达式转换为类型'TWidget'”。

我也尝试过让ConcreteFactory返回ConcreteWidget,但是错误是ConcreteFactory不符合Factory

1 个答案:

答案 0 :(得分:3)

这行不通。当您调用您的createWidget方法时,指定两种类型TTWidget

struct MyWidget: Widget { 
   func get() -> Int { ... }
}

let widget: MyWidget = factory.createWidget(12)

在此示例中,TWidgetMyWidget,而TInt。这很好地说明了为什么您的方法行不通。您不能将ConcreteWidget<Int>分配给类型MyWidget的变量。

您需要的是用于小部件的类型擦除器。当前,您必须自己编写该代码,但是将来,编译器有望在需要时自动生成它们。

struct AnyWidget<T>: Widget {
    private let _get: () -> T

    init<Other: Widget>(_ other: Other) where Other.T == T {
        _get = other.get
    }

    func get() -> T {
        return _get()
    }
}

这使您可以编写工厂协议和实现:

protocol Factory {
    func createWidget<T>(_ t: T) -> AnyWidget<T>
}

struct ConcreteFactory: Factory {
    func createWidget<T>(_ t: T) -> AnyWidget<T> {
            return AnyWidget(ConcreteWidget(widgetValue: t))
    }
}