我正在尝试创建一个定义单个方法的接口。该方法的参数类型必须是接口的类型参数,因为接口的实现将具有此参数的唯一实现。到目前为止很好,但问题是该参数还有一个与方法绑定的类型参数。我可以将类型的参数定义为类类型参数,它也有自己的类型参数与方法绑定吗?
换句话说:
我正在尝试使用两个实现构建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 -> ...)
是否可以创建这样的界面或者泛型不能那样工作?
答案 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
将其用作RequestImpl
:request as RequestImpl
。
按如下方式转换ApiClientImpl
:
class ApiClientImpl : ApiClient<RequestImpl<*>> {
override fun <T> rxecute(request: Request<RequestImpl<*>, T>): Single<T> = TODO()
}
这个解决方案看起来比你要求的更冗长,但递归泛型似乎是这种情况的常见解决方法,这需要比Java和Kotlin提供的更强大的类型系统。