2

在将我的 android 项目从 java 转换为 Kotlin 时,我遇到了 Kotlin 的一些问题。假设我有接口 I 和扩展接口 I 的接口 O。

interface I{

}

interface O: I{

}

泛型类 A 具有扩展接口 I 的泛型参数 V,泛型类 B 扩展类 A:

abstract class A<V: I> {

}

class B : A<O>() {

}

当我试图创建这样的属性时:

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

我收到编译器错误“需要 A,找到 B”。在 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 - View 通信:

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

这是我扩展这些类的示例代码:

授权视图模型:

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

       // ....

    }

认证导航器:

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,但编译器会抛出错误“required 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 类中设置它。我可以为此属性创建公共设置器吗?当我尝试创建 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
}
4

1 回答 1

3

看起来您期望类型参数的行为是协变的。Kotlin 使用声明站点差异。如果您不指定方差,则泛型类型参数是不变的。

换句话说,现在A<I>和之间没有关系A<O>。但是如果你声明

abstract class A<out V : I>

thenA<O>是 的子类型A<I>

(还有<in>逆变,它可以反过来工作。有关更多详细信息,请参阅https://kotlinlang.org/docs/reference/generics.html。)

于 2017-11-03T02:17:34.273 回答