在Kotlin混合类和函数泛型?

时间:2017-04-17 12:48:54

标签: generics kotlin

我正在尝试创建一个定义单个方法的接口。该方法的参数类型必须是接口的类型参数,因为接口的实现将具有此参数的唯一实现。到目前为止很好,但问题是该参数还有一个与方法绑定的类型参数。我可以将类型的参数定义为类类型参数,它也有自己的类型参数与方法绑定吗?

换句话说:

我正在尝试使用两个实现构建api客户端接口。每个客户端根据传递到Request<T>函数的rxecute对象进行api调用(RxJava +执行,是的,我对这个名称感到非常自豪)。

我的问题是如何在界面中定义rxecute方法。让我列出一些示例代码(我的实际代码不使用impl,不用担心)。

interface Request<T> // T is return type of the request

class RequestImpl<T>: Request<T> { // impl }

interface ApiClient<R : Request<*>> {
  // R should somehow be of type Request<T>
  fun <T> rxecute(request: R): Single<T>
}

class ApiClientImpl : ApiClient<RequestImpl<????>> {
  override fun <T> rxecute(request: RequestImpl<T>): Single<T> = // impl
}

我希望能够像这样使用它:

val apiClient = ApiClientImpl()
apiClient.rxecute(RequestImpl<SomeClass>()).subscribe(someClassResult -> ...)

是否可以创建这样的界面或者泛型不能那样工作?

1 个答案:

答案 0 :(得分:3)

不幸的是,Kotlin仿制药不能以这种方式操纵;类型参数R不能用作泛型类型本身(R<T>),并且其上限在声明站点处是固定的,并且您只能使用类型参数R作为单个上限(无法添加另一个,where Q : R, Q : Request<T>不起作用)。

您只能参数化具体类型,我想到的一个选项是recursive generics模式(它也用于Java)。您可以将实现类型的另一个类型参数添加到Request,以使其变为interface Request<R, T>。这要求您将实现的类型添加到继承的类型声明:

class RequestImpl<T>: Request<RequestImpl<*>, T> { /* impl */ }

然后修改ApiClient

interface ApiClient<R : Request<R, *>> {
    fun <T> rxecute(request: Request<R, T>): Single<T>
}

这里出现的一个不受欢迎的副作用是你需要强制request将其用作RequestImplrequest as RequestImpl

按如下方式转换ApiClientImpl

class ApiClientImpl : ApiClient<RequestImpl<*>> {
    override fun <T> rxecute(request: Request<RequestImpl<*>, T>): Single<T> = TODO()
}

这个解决方案看起来比你要求的更冗长,但递归泛型似乎是这种情况的常见解决方法,这需要比Java和Kotlin提供的更强大的类型系统。

(runnable demo of the code in the answer)