Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Generics 泛型:抽象类和子类的类型_Generics_Kotlin - Fatal编程技术网

Generics 泛型:抽象类和子类的类型

Generics 泛型:抽象类和子类的类型,generics,kotlin,Generics,Kotlin,我有一个名为presenter的抽象类: 抽象类演示器{ 趣味绑定(视图:V){ ... } ... } 我有这些演示者的实现: class FolderChooserPresenter:Presenter(){ ... } 以及在指定点调用bind方法的视图类: 类FolderChooseSactivity:BaseView(),FolderChooserView{ @注入lateinit变量presenter:FolderChooserPresenter //baseview方法 覆盖有

我有一个名为presenter的抽象类:

抽象类演示器{
趣味绑定(视图: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>()
}