user1321706
user1321706

Reputation: 117

Kotlin generic methods and inheritance

In my code I would like to create a method in an abstract class, which returns some Observable. Then implementation of this abstract class would return Observable of certain (specified) type. Unfortunately Android Studio will return an error "Type mismatch" in implementation method():

My MockDrawerList.getList() returns Observable<DrawerItemEntity>

please focus on execute() and buildUseCaseObservable

Abstract 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()
    }
}

Implementation

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

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

Upvotes: 1

Views: 3294

Answers (1)

Alexander Udalov
Alexander Udalov

Reputation: 32776

Even though String is a subtype of Any, Observable<String> is not considered a subtype of Observable<Any> in Kotlin. You should use use-site variance: change buildUseCaseObservable's return type to Observable<out Any> or Observable<*> (the latter is equivalent to Observable<out Any?>) and the code should compile.

Why doesn't inheritance for generic types work automatically based on their type parameters? This is a problem best explained by Joshua Bloch in Effective Java, Item 28: Use bounded wildcards to increase API flexibility. To put very simply, this can work only when the type is guaranteed not to consume instances of its type parameters, i.e. if a class with a generic parameter T has no methods taking T as a parameter. One way this can be enforced in Kotlin is by declaration-site variance, which is the case for example for collection interfaces (kotlin.Collection, kotlin.List, ...).

However, Observable is of course not a Kotlin class, so the compiler has no way of knowing that Observable<String> can be used safely where an Observable<Any> is expected. So we have to manually tell the compiler that we're OK with using only a subset of features of Observable<T> that don't take T as a parameter, so that we will get the nice inheritance instead. This "subset type" is called a type projection in Kotlin and the technique is called use-site variance.

You can read more about declaration- and use-site variance in Kotlin in the official language documentation, section Generics.

Upvotes: 7

Related Questions