Reputation: 3349
I've defined the following base class with two generic types and using it two levels deep (for lack of a better phrase). Here's my use case.
abstract class BasePresenter<out M, out V> {
var model: M? = null
var view: WeakReference<V>? = null
fun setM(model: M?): Unit {
this.model = model
if (setupDone()) {
updateView()
}
}
fun bindView(view: V) {
this.view = WeakReference(view)
}
fun unbindView() {
this.view = null
}
abstract fun updateView()
fun view(): V? {
return if (view == null) null else view?.get()
}
fun setupDone(): Boolean {
return view() != null && model != null
}
}
I'm extending it using
open class VenueListPresenter: BasePresenter<Venue, VenueView>() {...}
Which works fine, as expected, but then I'm running into issues when I'm trying to use the VenuListPresenter
as a type parameter in a different class.
open class VenuesViewHolder(itemView: View): MvpViewHolder<VenueListPresenter>(itemView) {
This gives me an error stating that the expected argument in MvpViewHolder
is BasePresenter, and that what was found was a VenueListPresenter
. My VenueListPresenter
extends a BasePresenter<Venue, VenueView>
where Venue
and VenueView
are of type Any?
because by default they extend it. So why isn't it working?
MvpViewHolder is defined like so
abstract class MvpViewHolder<P>(itemView: View) : RecyclerView.ViewHolder(itemView) where P : BasePresenter<Any?, Any?>
Upvotes: 1
Views: 1669
Reputation: 89658
You need to add out
variance to your generic parameters in BasePresenter
so that a type like BasePresenter<Venue, VenueView>
will be a subtype of BasePresenter<Any?, Any?>
.
abstract class BasePresenter<out M, out V>
As a short explanation, the out
keyword specifies covariance, e.g. if you have a class YourClass<out T>
, it means that when A
is a subtype of B
, then YourClass<A>
is also a subtype of YourClass<B>
.
See more details about Kotlin generics and variance in the docs.
Edit based on comment below:
If you can't make the above change, you could use use site variance at the MvpViewHolder
class instead, to accept BasePresenter
subtypes with any subtypes of Any?
in their type parameters:
abstract class MvpViewHolder<P>(itemView: View) : RecyclerView.ViewHolder(itemView)
where P : BasePresenter<out Any?, out Any?>
You could do this exact same thing with star projection (just a different syntax in this case):
abstract class MvpViewHolder<P>(itemView: View) : RecyclerView.ViewHolder(itemView)
where P : BasePresenter<*, *>
In both of these cases, anything of type V
or M
returned from a P
will be of type Any?
, and you won't be able to pass V
or M
instances into methods of P
. If you need to be able to do that, you could consider adding more generic parameters:
abstract class MvpViewHolder<P, M, V>(itemView: View) : RecyclerView.ViewHolder(itemView)
where P : BasePresenter<M, V>
Upvotes: 3