Kotlin 泛型属性问题
Kotlin generic properties issue
在将我的 android 项目从 java 转换为 Kotlin 时,我遇到了一些 Kotlin 问题。
假设我有接口 I 和扩展接口 I 的接口 O。
interface I{
}
interface O: I{
}
和泛型 class A 具有扩展接口 I 的泛型参数 V,以及扩展 class A:
的泛型 class B
abstract class A<V: I> {
}
class B : A<O>() {
}
当我尝试创建这样的 属性:
val returnB: A<I>
get() = b
我遇到编译器错误 'required A, found B'。在 Java 中,这将毫无问题地工作。我如何使用 Kotlin 访问它?
我需要在我的应用程序中对 Basic classes 使用这种方法。
具有 Navigator 通用参数的 BaseViewModel class:
abstract class BaseViewModel<N>(application: Application, val repositoryProvider:
RepositoryProvider) : AndroidViewModel(application) {
var navigator: N? = null
fun onDestroyView() {
navigator = null
}
open fun onViewAttached() {
}
}
基础Activity 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 界面用于 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?)
}
这里是我扩展这些 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>)
}
和AuthActivity class出现错误的地方:
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
但在这种情况下编译器会抛出错误“属性 type is 'AuthViewModel', which is not a subtype type of overridden'.
更新:
当我将 属性 添加到 BaseViewModel:
时有效
BaseViewModel<out N : BaseNavigator>
但在这种情况下我只能创建
private var navigator: N? = null
我需要 public 这样我就可以在 Activity class 中设置它。我可以为这个 属性 创建 public 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
}
您似乎希望类型参数表现得协变。 Kotlin 使用声明站点变量。如果不指定方差,泛型类型参数是invariant.
也就是说,现在A<I>
和A<O>
之间没有关系。但是如果你声明
abstract class A<out V : I>
那么 A<O>
是 A<I>
的子类型。
(还有 <in>
用于逆变,反之亦然。有关详细信息,请参阅 https://kotlinlang.org/docs/reference/generics.html。)
在将我的 android 项目从 java 转换为 Kotlin 时,我遇到了一些 Kotlin 问题。 假设我有接口 I 和扩展接口 I 的接口 O。
interface I{
}
interface O: I{
}
和泛型 class A 具有扩展接口 I 的泛型参数 V,以及扩展 class A:
的泛型 class Babstract class A<V: I> {
}
class B : A<O>() {
}
当我尝试创建这样的 属性:
val returnB: A<I>
get() = b
我遇到编译器错误 'required A, found B'。在 Java 中,这将毫无问题地工作。我如何使用 Kotlin 访问它?
我需要在我的应用程序中对 Basic classes 使用这种方法。
具有 Navigator 通用参数的 BaseViewModel class:
abstract class BaseViewModel<N>(application: Application, val repositoryProvider:
RepositoryProvider) : AndroidViewModel(application) {
var navigator: N? = null
fun onDestroyView() {
navigator = null
}
open fun onViewAttached() {
}
}
基础Activity 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 界面用于 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?)
}
这里是我扩展这些 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>)
}
和AuthActivity class出现错误的地方:
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
但在这种情况下编译器会抛出错误“属性 type is 'AuthViewModel', which is not a subtype type of overridden'.
更新: 当我将 属性 添加到 BaseViewModel:
时有效BaseViewModel<out N : BaseNavigator>
但在这种情况下我只能创建
private var navigator: N? = null
我需要 public 这样我就可以在 Activity class 中设置它。我可以为这个 属性 创建 public 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
}
您似乎希望类型参数表现得协变。 Kotlin 使用声明站点变量。如果不指定方差,泛型类型参数是invariant.
也就是说,现在A<I>
和A<O>
之间没有关系。但是如果你声明
abstract class A<out V : I>
那么 A<O>
是 A<I>
的子类型。
(还有 <in>
用于逆变,反之亦然。有关详细信息,请参阅 https://kotlinlang.org/docs/reference/generics.html。)