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

我有一个叫做演示者的抽象类:

abstract class Presenter<V> { fun bind(view: V) { ... } ... } 

我有这些演示者的实现:

 class FolderChooserPresenter : Presenter<FolderChooserView>() { ... } 

并查看在指定点调用绑定方法的类:

 class FolderChooserActivity : BaseView(), FolderChooserView { @Inject lateinit var presenter: FolderChooserPresenter // method of baseview override fun onStart() { super.onStart() presenter.bind(this) } } 

我想实现的是有一个像FolderChooserActivity这样的类自动调用绑定方法的基类。
在所有的实现中重复这些调用是愚蠢的。

我的方法是有一个抽象类来扩展调用bind方法的BaseView 。 但是这显然不起作用,因为绑定类需要实现而不是抽象类。

您可以将两个通用参数添加到BaseView类中,并将thisV

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

你会用这个

 class FolderPresenter : Presenter<FolderChooserView>() { } class FolderChooserView : BaseView<FolderPresenter, FolderChooserView>() 

不幸的是,如果你混淆了参数,你将不会从你的编译器得到任何帮助,因为未经检查的转换:

 class SomeOtherView : BaseView<SomeOtherPresenter, FolderChooserView>() 

@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>() 

使用函数来创建类,并进行编译时检查以避免运行时错误

你也可以要求使用一个函数来构造*Activity类。 但是如果你不能强制人们确保他们有正确的视图基类,那么你就不能强制使用这个函数。 无论如何,只是为了给这个问题提供更多的想法。

 inline fun <reified A: V, P: Presenter<V>, V : View> makeActivity(): A { return A::class.java.newInstance() } // 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>() 

完整代码:

我在这里把完整的代码,所以我可以探索这个更多,并尝试得到一个替代完整的答案。 这实际上只是@ 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>() }