泛型:抽象类和孩子的类型
我有一个叫做演示者的抽象类:
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
类中,并将this
为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) } }
你会用这个
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>() }