Generics 泛型:抽象类和子类的类型
我有一个名为presenter的抽象类:Generics 泛型:抽象类和子类的类型,generics,kotlin,Generics,Kotlin,我有一个名为presenter的抽象类: 抽象类演示器{ 趣味绑定(视图:V){ ... } ... } 我有这些演示者的实现: class FolderChooserPresenter:Presenter(){ ... } 以及在指定点调用bind方法的视图类: 类FolderChooseSactivity:BaseView(),FolderChooserView{ @注入lateinit变量presenter:FolderChooserPresenter //baseview方法 覆盖有
抽象类演示器{
趣味绑定(视图:V){
...
}
...
}
我有这些演示者的实现:
class FolderChooserPresenter:Presenter(){
...
}
以及在指定点调用bind方法的视图类:
类FolderChooseSactivity:BaseView(),FolderChooserView{
@注入lateinit变量presenter:FolderChooserPresenter
//baseview方法
覆盖有趣的onStart(){
super.onStart()
presenter.bind(此)
}
}
我想归档的是为像folderChooseSectorActivity
这样的类创建一个基类,它会自动调用bind方法。在所有实现中一遍又一遍地重复这些调用感觉很愚蠢
我的方法是使用一个抽象类来扩展调用bind方法的
BaseView
。但这显然不起作用,因为绑定类需要实现,而不是抽象类。您可以向BaseView
类添加两个通用参数,并将此
转换为V
:
open class Presenter<V> {
fun bind(v: V) {}
}
open class BaseView<P, V> where P : Presenter<V> {
lateinit var presenter: P
fun onStart() {
p.bind(this as V)
}
}
开放式课堂演示者{
有趣的绑定(v:v){}
}
打开类BaseView,其中P:Presenter{
lateinit变量呈现者:P
有趣的开始(){
p、 绑定(此为V)
}
}
你会像这样使用这个吗
class FolderPresenter : Presenter<FolderChooserView>() {
}
class FolderChooserView : BaseView<FolderPresenter, FolderChooserView>()
class FolderPresenter:Presenter(){
}
类FolderChooserView:BaseView()
不幸的是,由于未经检查的强制转换,如果混淆了参数,您将无法从编译器获得任何帮助:
class SomeOtherView : BaseView<SomeOtherPresenter, FolderChooserView>()
class-SomeOtherView:BaseView()
来自@nhaarman的答案很接近,但如果要绑定的类实际上不是视图的类型,则留下了漏洞
这个演员阵容并不是一件坏事,如果有的话,也没有很多好的答案。您始终可以添加一个断言,该断言会给出一个真正直接的错误消息,并且由于这些错误在创建活动时立即发生,所以您将在测试的早期看到错误 我认为如果你不创造一个整体管理关系所有部分的东西,你就很难找到更好的答案。我认为他的回答风险很小 编译时检查以避免运行时错误 您可以编写一个函数,在编译时检查缺失的部分,人们可以像编译时断言一样使用它
// a function that is used when people want to check validity at compile time,
// it does nothing but cause compiler error if wrong heirarchy
fun <A: V, P: Presenter<V>, V : View> checkValid() {
// empty on purpose, used for compile time check
}
// successful:
checkValid<FolderChooserActivity, FolderPresenter, FolderChooserView>()
// error below: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.FolderChooserView'"
checkValid<TryingToFoolItActivity, FolderPresenter, FolderChooserView>()
// this is blocked because SomeOtherActivity has its own compiler error so SomeOtherActivity type is not fully known
checkValid<SomeOtherActivity, FolderPresenter, FolderChooserView>()
完整代码:
我把完整的代码放在这里,这样我可以更深入地探讨这个问题,并尝试获得一个完整的答案。这实际上只是@nhaarman答案的一个变体
// sample classes
class FolderPresenter : Presenter<FolderChooserView>() { }
class BadPresenter : Presenter<RandomView>() { }
// successful declaration
class FolderChooserActivity : BaseActivity<FolderPresenter, FolderChooserView>(), FolderChooserView { }
// Error: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.Presenter<test.renlearn.solrpref.FolderChooserView>'"
class SomeOtherActivity : BaseActivity<BadPresenter, FolderChooserView>(), FolderChooserView {}
// Runtime error, we are not a FolderChooserView
class TryingToFoolItActivity : BaseActivity<FolderPresenter, FolderChooserView>() {}
// now the version using a function to construct the activity, where
// this function adds the missing step of compiler time validation.
inline fun <reified A: V, P: Presenter<V>, V : View> makeActivity(): A {
return A::class.java.newInstance()
}
// or a function that is used when people want to check validity at compile time,
// it does nothing but cause compiler error if wrong heirarchy
fun <A: V, P: Presenter<V>, V : View> checkValid() {
// empty on purpose, used for compile time check
}
public fun foo() {
// successful:
val good1 = makeActivity<FolderChooserActivity, FolderPresenter, FolderChooserView>()
// error below: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.FolderChooserView'"
val bad1 = makeActivity<TryingToFoolItActivity, FolderPresenter, FolderChooserView>()
// this is blocked because SomeOtherActivity has its own compiler error so SomeOtherActivity type is not fully known
val bad2 = makeActivity<SomeOtherActivity, FolderPresenter, FolderChooserView>()
// successful:
checkValid<FolderChooserActivity, FolderPresenter, FolderChooserView>()
// error below: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.FolderChooserView'"
checkValid<TryingToFoolItActivity, FolderPresenter, FolderChooserView>()
// this is blocked because SomeOtherActivity has its own compiler error so SomeOtherActivity type is not fully known
checkValid<SomeOtherActivity, FolderPresenter, FolderChooserView>()
}
//示例类
类FolderPresenter:Presenter(){}
类BadPresenter:Presenter(){}
//成功申报
类FolderChooseActivity:BaseActivity(),FolderChooserView{}
//错误:“Kotlin:类型参数不在其范围内:应为'test.so.Presenter'的子类型”
类SomeOtherActivity:BaseActivity(),FolderChooserView{}
//运行时错误,我们不是FolderChooserView
类TryingToFoolItActivity:BaseActivity(){}
//现在是使用函数构造活动的版本,其中
//此函数添加缺少的编译器时间验证步骤。
inline fun makeActivity():A{
返回一个::class.java.newInstance()
}
//或者当人们想在编译时检查有效性时使用的函数,
//如果继承权错误,它只会导致编译器错误
fun checkValid(){
//故意为空,用于编译时检查
}
公众同乐坊(){
//成功的:
val good1=makeActivity()
//下面的错误:“Kotlin:类型参数不在其范围内:应为'test.so.FolderChooserView'的子类型”
val bad1=makeActivity()
//这被阻止,因为SomeOtherActivity有自己的编译器错误,因此SomeOtherActivity类型不完全已知
val bad2=makeActivity()
//成功的:
checkValid()
//下面的错误:“Kotlin:类型参数不在其范围内:应为'test.so.FolderChooserView'的子类型”
checkValid()
//这被阻止,因为SomeOtherActivity有自己的编译器错误,因此SomeOtherActivity类型不完全已知
checkValid()
}
Hm,我不喜欢这个。我正在考虑将其设计为一个通用库,因此我真的希望避免此类未经检查的强制转换。对于我想要实现的目标,难道没有解决方案吗?我也处理过这个问题,并且找到了一个合适的解决方案,结果证明这是一条通往复杂的道路。请参阅,和base(java)。这些是我的库的较旧版本的文件,其中Presenter和View的泛型类型相互引用以强制执行正确的行为。不幸的是,这个演员阵容并不是一件坏事,如果有的话,也没有很多好的答案。您始终可以添加一个断言,该断言会给出一条真正直接的错误消息,并且由于这些错误在创建活动时立即发生,您将在测试的早期看到错误。是的,这就是我选择这种方式的原因。FolderChooserView
是一个接口,所有视图接口都有一个共同的祖先接口吗?添加android
标签可能会有所帮助。如果这是android类层次结构中的一个常见问题,其他人可能已经解决了。如果用Java泛型解决,Kotlin的解决方案很可能是相同的。您可能还想用java
标记询问它,然后编辑问题,说对于java或Kotlin,您将如何使用泛型解决这个问题,同时避免可能的运行时未检查强制转换。它不需要局限于Kotlin。是的,它是一个接口。不,它们没有共同的祖先接口。但如果这能解决问题,并绕过强制转换,这是一个选项。事实上,这并不能解决问题,你将从祖先到后代进行强制转换,这仍然需要未经检查的强制转换。看到下面我的答案,我试过了。
// sample classes
class FolderPresenter : Presenter<FolderChooserView>() { }
class BadPresenter : Presenter<RandomView>() { }
// successful declaration
class FolderChooserActivity : BaseActivity<FolderPresenter, FolderChooserView>(), FolderChooserView { }
// Error: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.Presenter<test.renlearn.solrpref.FolderChooserView>'"
class SomeOtherActivity : BaseActivity<BadPresenter, FolderChooserView>(), FolderChooserView {}
// Runtime error, we are not a FolderChooserView
class TryingToFoolItActivity : BaseActivity<FolderPresenter, FolderChooserView>() {}
// now the version using a function to construct the activity, where
// this function adds the missing step of compiler time validation.
inline fun <reified A: V, P: Presenter<V>, V : View> makeActivity(): A {
return A::class.java.newInstance()
}
// or a function that is used when people want to check validity at compile time,
// it does nothing but cause compiler error if wrong heirarchy
fun <A: V, P: Presenter<V>, V : View> checkValid() {
// empty on purpose, used for compile time check
}
public fun foo() {
// successful:
val good1 = makeActivity<FolderChooserActivity, FolderPresenter, FolderChooserView>()
// error below: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.FolderChooserView'"
val bad1 = makeActivity<TryingToFoolItActivity, FolderPresenter, FolderChooserView>()
// this is blocked because SomeOtherActivity has its own compiler error so SomeOtherActivity type is not fully known
val bad2 = makeActivity<SomeOtherActivity, FolderPresenter, FolderChooserView>()
// successful:
checkValid<FolderChooserActivity, FolderPresenter, FolderChooserView>()
// error below: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.FolderChooserView'"
checkValid<TryingToFoolItActivity, FolderPresenter, FolderChooserView>()
// this is blocked because SomeOtherActivity has its own compiler error so SomeOtherActivity type is not fully known
checkValid<SomeOtherActivity, FolderPresenter, FolderChooserView>()
}