实现协议方法

时间:2016-02-05 20:06:45

标签: swift generics

我有一个名为ContentService的协议,旨在公开从各种REST API读取数据的常用功能。 ContentService的每个实现都将执行该特定API所需的机制。

protocol ContentService {     
    func loadNextPage<T>(pageStartId: T) -> [Datum]
    //Other methods...
}

由于给定的API可能有不同的标记返回数据页边界的方式,我想在实现类中指定所需的类型,如下所示:

class ServiceA : ContentService {

    func loadNextPage<Int>(pageStartId: Int) -> [Datum] {
        //Do something with pageStartId typed as an Int
        return posts
    }
}

class ServiceB : ContentService {
    func loadNextPage<NSDate>(pageStartId: NSDate) -> [Datum] {
        //Do something with pageStartId typed as an NSDate
        return posts
    }
}

注意我在实现类型(ServiceA,ServiceB)的定义中指定了type参数,因为它们特定于该服务。我的期望是这些类的任何实例都有一个签名为loadNextPage(pageStartId: X) -> [Post]的方法,其中X是实现该方法时为T提供的特定类型。

为了从api加载一页数据,我会使用这样的东西:

let apiA = ServiceA()
let apiB = ServiceB()

let dataA = apiA.loadNextPage(1234)
let dataB = apiB.loadNextPage(NSDate())

虽然这个编译没有错误。您还可以编译以下内容而不会出现错误:

let dataA = apiA.loadNextPage("Anything works here.")

因此,ServiceA的loadNextPage()方法不限于Int参数。此外,在loadNextPage()方法的方法定义中,虽然在Xcode中检查它时,pageStartId参数似乎是预期的类型,但我不能使用通常在该类型上可访问的任何运算符或方法。例如,在ServiceA的实现中,即使pageStartId应该是Int,我也无法let newId = pageStartId + 5

两个问题:

1)显然我对Swift中的泛型方法有些误解,并且想知道为什么不能使用这种模式。

2)如果有人有一个聪明的解决方案来实现我想要上面的尝试,我很想知道它。

1 个答案:

答案 0 :(得分:4)

您的协议

protocol ContentService {     
    func loadNextPage<T>(pageStartId: T) -> [Datum]
    //Other methods...
}

需要通用方法loadNextPage

func loadNextPage<Int>(pageStartId: Int) -> [Datum] {
    //Do something with pageStartId typed as an Int
    return posts
}

就是这样:占位符碰巧的通用方法 名字为Int。在方法内部,Int引用该占位符类型,隐藏全局Swift类型Int。等价的定义是

func loadNextPage<FOO>(pageStartId: FOO) -> [Datum] {
    //Do something with pageStartId typed as an Int
    return posts
}

您可能想要的是使用关联类型 T定义协议:

protocol ContentService { 
    associatedtype T
    func loadNextPage(pageStartId: T) -> [Datum]
    //Other methods...
}

和一个采用T == Int协议的类:

class ServiceA : ContentService {

    func loadNextPage(pageStartId: Int) -> [Datum] {
        let newId = pageStartId + 5  // <-- This compiles now!
        //Do something with pageStartId typed as an Int
        return posts
    }
}

现在

let dataA = apiA.loadNextPage(1234)

编译,但

let dataA2 = apiA.loadNextPage("Anything works here.")
// error: cannot convert value of type 'String' to expected argument type 'Int'

没有像预期的那样。