Kotlin通用属性问题

时间:2017-11-02 22:42:55

标签: android generics inheritance kotlin kotlin-generics

在将我的android项目从java翻译成Kotlin时,我遇到了Kotlin的一些问题。 假设我有接口I和接口O,它扩展了接口I.

interface I{

}

interface O: I{

}

泛型类A,它具有扩展interfaceI的泛型参数V,以及扩展了类A的泛型类B:

abstract class A<V: I> {

}

class B : A<O>() {

}

当我试图创造这样的财产时:

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

我收到编译错误&#39;要求A,找到B&#39;。在Java中,这将没有任何问题。我如何使用Kotlin访问它?

我需要在我的应用程序中对Basic类使用这种方法。

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接口用于VM - 查看通信:

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,但编译器抛出错误&#39;必需的BaseViewModel&#39;。

And i tried to change

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

to 

override val viewModel: AuthViewModel
        get() = mViewModel

但是在这种情况下编译器抛出错误&#39;属性类型是&#39; AuthViewModel&#39;,这不是被覆盖的子类型&#39;。

更新 当我向BaseViewModel添加属性时,这是有效的:

BaseViewModel<out N : BaseNavigator>

但在这种情况下我只能创建

private var navigator: N? = null

我需要公开,所以我可以在Activity类中设置它。我可以为这个属性创建公共setter吗?当我试图创建setter时会发生错误:

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 个答案:

答案 0 :(得分:2)

看起来你期望type参数表现为 covariantly 。 Kotlin使用声明站点方差。如果未指定方差,则泛型类型参数为不变

换句话说,目前A<I>A<O>之间没有任何关系。但是如果你宣布

abstract class A<out V : I>

然后A<O>A<I>的子类型。

(还有<in>用于反演,反过来也是如此。有关详细信息,请参阅https://kotlinlang.org/docs/reference/generics.html。)