Вопрос об общих свойствах Kotlin

У меня возникли некоторые проблемы с Kotlin при переводе моего проекта Android из Java в Kotlin. Скажем, у меня есть интерфейс I и интерфейс O, который расширяет интерфейс I.

interface I{

}

interface O: I{

}

И универсальный класс A, который имеет универсальный параметр V, который расширяет interfaceI, и универсальный класс B, который расширяет класс A:

abstract class A<V: I> {

}

class B : A<O>() {

}

Когда я пытаюсь создать такую ​​собственность:

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

Я получаю сообщение об ошибке компилятора "требуется A, найдено B". В Java это будет работать без проблем. Как я могу получить доступ к этому с помощью Kotlin?

Мне нужно использовать этот подход для базовых классов в моем приложении.

BaseViewModel, у которого есть универсальный параметр для класса Navigator:

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

    var navigator: N? = null

    fun onDestroyView() {
        navigator = null
    }

    open fun onViewAttached() {

    }
}

Класс BaseActivity:

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 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?)
}

Вот пример кода, где я расширяю эти классы:

AuthViewModel:

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

       // ....

    }

AuthNavigator:

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

    fun requestFacebookAuth(callback: FacebookCallback<LoginResult>)
}

И класс AuthActivity, где появилась ошибка:

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

}

Я также пытался изменить общий параметр в AuthActivity с BaseViewModel на AuthViewModel, но компилятор выдает ошибку "Требуется BaseViewModel".

And i tried to change

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

to 

override val viewModel: AuthViewModel
        get() = mViewModel

но в этом случае компилятор выдает ошибку "Тип свойства -" AuthViewModel ", который не является переопределенным типом подтипа".

обновление: это работает, когда я добавляю свойство в BaseViewModel:

BaseViewModel<out N : BaseNavigator>

Но в этом случае я могу только создать

private var navigator: N? = null

который я должен быть публичным, чтобы я мог установить его в классе Activity. Могу ли я создать публичный сеттер для этого свойства? Когда я пытаюсь создать сеттер, возникает ошибка:

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
}

1 ответ

Решение

Похоже, вы ожидаете, что параметр типа будет вести себя ковариантно. Kotlin использует декларацию-сайт дисперсии. If you do not specify the variance, generic type parameters are invariant.

In other words, right now there is no relationship between A<I> а также A<O>, But if you declare

abstract class A<out V : I>

затем A<O> это подтип A<I>,

(Существует также <in> for contravariance, which works the other way around. See https://kotlinlang.org/docs/reference/generics.html for more details.)

Другие вопросы по тегам