Search code examples
androidgenericsinheritancekotlinkotlin-generics

Kotlin generic properties issue


I got some issues with Kotlin when translating my android project from java to Kotlin. Say i have interface I and interface O which extends interface I.

interface I{

}

interface O: I{

}

And generic class A which have generic parameter V that extends interfaceI, and generic class B which extends class A:

abstract class A<V: I> {

}

class B : A<O>() {

}

When i'm trying to create such property:

val returnB: A<I>
    get() = b

I'm getting compiler error 'required A, found B'. In Java this will work without any issues. How can i access this using Kotlin ?

I need to use this approach for Basic classes in my application.

BaseViewModel which have generic parameter for Navigator class:

abstract class BaseViewModel<N>(application: Application, val repositoryProvider:
RepositoryProvider) : AndroidViewModel(application) {

    var navigator: N? = null

    fun onDestroyView() {
        navigator = null
    }

    open fun onViewAttached() {

    }
}

BaseActivity class:

abstract class BaseActivity<T : ViewDataBinding, V : BaseViewModel<BaseNavigator>> : AppCompatActivity(),
        BaseFragment.Callback, BaseNavigator {

    // .......

    private var mViewModel: V? = null

    /**
     * Override for set view model
     * @return view model instance
     */
    abstract val viewModel: V

    // .......
}

BaseNavigator interface uses for VM - View communication:

interface BaseNavigator {

    fun invokeIntent(intent: Intent?, b: Bundle?, c: Class<*>?,
                     forResult: Boolean, requestCode: Int)

    fun replaceFragment(fragment: Fragment, addToBackStack: Boolean)

    fun showDialogFragment(fragment: DialogFragment?, tag: String?)

    fun showToast(message: String?)
}

Here example code where i'm extending these classes:

AuthViewModel:

class AuthViewModel(context: Application, repositoryProvider: RepositoryProvider) : 
BaseViewModel<AuthNavigator>(context,repositoryProvider) {

       // ....

    }

AuthNavigator:

interface AuthNavigator : BaseNavigator {
    fun requestGoogleAuth(code: Int)

    fun requestFacebookAuth(callback: FacebookCallback<LoginResult>)
}

And AuthActivity class where error was appeared:

class AuthActivity : BaseActivity<ActivityAuthBinding, BaseViewModel<BaseNavagator>>(),
        GoogleApiClient.OnConnectionFailedListener, AuthNavigator {

@Inject
lateinit var mViewModel: AuthViewModel    

override val viewModel: BaseViewModel<BaseNavigator>
    get() = mViewModel     // Required:BaseViewModel<BaseNavigator> Found:  AuthViewModel

}

I'm also tried to change generic parameter in AuthActivity from BaseViewModel to AuthViewModel, but compiler throws error 'required BaseViewModel'.

And i tried to change

override val viewModel: BaseViewModel<BaseNavigator>
        get() = mViewModel

to 

override val viewModel: AuthViewModel
        get() = mViewModel

but in this case compiler throws error 'Property type is 'AuthViewModel', which is not a subtype type of overridden'.

update: That works when i add out property to BaseViewModel:

BaseViewModel<out N : BaseNavigator>

But in this case i can only create

private var navigator: N? = null

which i need to be public so i can set it in the Activity class. Can i create public setter for this property? When i'm trying to create setter an error occurs:

private var navigator: N? = null

fun setNavigator(n: N) {   // error: Type parameter N is declared as 'out' but occurs in 'in' position in type N
    navigator = n
}

Solution

  • It looks like you are expecting the type parameter to behave covariantly. Kotlin uses declaration-site variance. If you do not specify the variance, generic type parameters are invariant.

    In other words, right now there is no relationship between A<I> and A<O>. But if you declare

    abstract class A<out V : I>
    

    then A<O> is a subtype of A<I>.

    (There is also <in> for contravariance, which works the other way around. See https://kotlinlang.org/docs/reference/generics.html for more details.)