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

时间:2015-12-30 08:50:30

标签: generics kotlin

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

abstract class Presenter<V> {

    fun bind(view: V) {
        ...
    }

    ...
}

我有这些演示者的实现:

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

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

class FolderChooserActivity : BaseView(), FolderChooserView {

    @Inject lateinit var presenter: FolderChooserPresenter

    // method of baseview
    override fun onStart() {
        super.onStart()

        presenter.bind(this)
    }
}

我想要的是为FolderChooserActivity这样的类创建一个基类,它自动调用bind方法。
在所有实现中一遍又一遍地重复这些调用感觉很愚蠢。

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

2 个答案:

答案 0 :(得分:0)

您可以向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>()

不幸的是,如果混淆参数,由于未选中的强制转换,您无法从编译器获得任何帮助:

 var shell = PowerShell.Create();
 shell.Commands.AddScript("Import-Module ServiceFabric");
 shell.Commands.AddScript("Connect-ServiceFabricCluster");
 var result = shell.Invoke();

答案 1 :(得分:0)

来自@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>()
}