Kotlin 泛型方法和继承

Kotlin generic methods and inheritance

在我的代码中,我想在抽象 class 中创建一个方法,其中 return 是一些 Observable。然后这个抽象 class 的实现将 return 某些(指定)类型的 Observable。不幸的是 Android Studio 将 return 在实现 method():

中出错 "Type mismatch"

我的 MockDrawerList.getList() returns Observable<DrawerItemEntity>

请关注execute()buildUseCaseObservable

摘要class

public abstract class UseCase(threadExecutor: ThreadExecutor,
    postExecutionThread: PostExecutionThread) {

    private val threadExecutor: ThreadExecutor
    private val postExecutionThread: PostExecutionThread

    private var subscription: Subscription = Subscriptions.empty()

    init {
        this.postExecutionThread = postExecutionThread
        this.threadExecutor = threadExecutor
    }

    protected abstract fun buildUseCaseObservable(): Observable<Any>

    public fun execute(useCaseSubsriber: Subscriber<Any>) {
        subscription = buildUseCaseObservable()
                .subscribeOn(Schedulers.from(threadExecutor))
                .observeOn(postExecutionThread.getScheduler())
                .subscribe(useCaseSubsriber)
    }

    public fun unsubsribe() {
        if (!subscription.isUnsubscribed())
            subscription.unsubscribe()
    }
}

实施

Inject
class GetDrawerListUseCase(threadExecutor: ThreadExecutor, 
postExecutionThread:PostExecutionThread) : UseCase(threadExecutor, postExecutionThread) {

    override fun buildUseCaseObservable(): Observable<Any> {
        return MockDrawerList.getList()
    }
}

尽管 StringAny 的子类型,但 Observable<String> 在 Kotlin 中 而不是 被视为 Observable<Any> 的子类型.您应该使用 use-site variance:将 buildUseCaseObservable 的 return 类型更改为 Observable<out Any>Observable<*>(后者相当于Observable<out Any?>),代码应该可以编译。

为什么泛型类型的继承不能根据它们的类型参数自动工作?这是 Joshua Bloch 在 Effective Java,Item 28: Use bounded wildcards to increase API flexibility 中最好解释的问题。简而言之,只有当类型保证不使用其类型参数的实例时,这才有效,即如果带有泛型参数 T 的 class 没有将 T 作为参数的方法一个参数。在 Kotlin 中可以强制执行的一种方法是 declaration-site variance,例如集合接口(kotlin.Collectionkotlin.List、...)就是这种情况。

但是,Observable 当然不是 Kotlin class,因此编译器无法知道 Observable<String> 可以安全地用于 Observable<Any>预期的。因此,我们必须手动告诉编译器我们可以只使用 Observable<T> 的一部分不将 T 作为参数的功能,这样我们将获得很好的继承。这种 "subset type" 在 Kotlin 中称为 type projection,该技术称为使用位置差异。

您可以在官方语言文档的 Generics.

部分中阅读有关 Kotlin 中声明和使用位置差异的更多信息