Kotlin开发Android时无法用继承的Presenter重写父类presenter属性怎么办
错误本质是Kotlin泛型的不变性约束:你在基类ListFragment中定义presenter的类型时,将ListPresenter的第二个泛型参数固定为ListFragment<ItemType, Adapter>,而子类重写时传入的是ContactsFragment。虽然ContactsFragment是ListFragment<Contact, ContactsAdapter>的子类,但默认情况下ListPresenter<Contact, ContactsFragment>和ListPresenter<Contact, ListFragment<Contact, ContactsAdapter>>不存在继承关系,因此触发类型不匹配错误。
方案1:给基类增加自身泛型参数(最稳妥,推荐)
修改ListFragment定义,增加第三个泛型参数指代自身类型,让presenter的泛型和子类实际类型绑定,强转逻辑因为子类泛型参数必然是自身,所以完全安全:
// 增加第三个泛型参数F,约束为当前ListFragment的子类 abstract class ListFragment<ItemType, Adapter : ListAdapter<ItemType>, F : ListFragment<ItemType, Adapter, F>> : BaseFragment(), ListContract.View<ItemType> { // presenter的第二个泛型参数改为F,绑定子类实际类型 protected open val presenter: ListPresenter<ItemType, F> by lazy { ListPresenter(this as F) } ... }
修改ContactsFragment的继承声明,传入自身作为第三个泛型参数即可:
// 第三个泛型参数填ContactsFragment自身 class ContactsFragment : ListFragment<Contact, ContactsAdapter, ContactsFragment>(), ContactsContract.View { // 此时重写的presenter类型完全匹配,不会报错 override val presenter: ContactsPresenter<ContactsFragment> by lazy { ContactsPresenter(this) } ... }
方案2:给ListPresenter的View泛型添加协变修饰
如果不想改动基类的泛型参数,且你的ListPresenter中没有把V类型作为方法入参使用的场景,可以给ListPresenter的第二个泛型参数加out关键字声明协变:
// 第二个泛型参数加out修饰,声明协变 open class ListPresenter<ItemType, out V : ListContract.View<ItemType>>(view: V) : BasePresenter<V>(view), ListContract.Presenter<ItemType, V> { ... }
修改后ListPresenter<Contact, ContactsFragment>会被自动识别为ListPresenter<Contact, ListFragment<Contact, ContactsAdapter>>的子类,类型错误直接消失。
内容的提问来源于stack exchange,提问作者Choo loo

