I trying to write some generic code, but can't get rid of Type of 'PROPERTY' is not a subtype of the overridden property
error.
Simplified version of my code:
abstract class BaseP<V> {
var view: V? = null
}
abstract class BaseF {
fun smth() {
pp.view = this
}
abstract val pp: BaseP<BaseF>
}
abstract class SubF: BaseF() {
abstract override val pp: BaseP<SubF>
// Error:(20, 30) Type of 'pp' is not a subtype of the overridden property 'public abstract val pp: BaseP<BaseF> defined in BaseF'
}
I found that error can be @Suppress
-ed, but I doubt it is best and only way. Is there something better?
And after all I can't understand, why subtypeA<subtypeB>
doesn't count as subtype of baseA<baseB>
, can someone explain this?
First, SubtypeA<B>
is a subtype of BaseA<B>
, so the problem is in the generics parameters subtyping.
The answer lies in Kotlin generics variance, which is similar to that of Java.
Why doesn't SubtypeA<SubtypeB>
count as subtype of BaseA<BaseB>
?
The generics are invariant by default, which means that, even in simpler case, for a class A<T>
, A<SubtypeB>
and A<BaseB>
are not subtypes of each other unless otherwise is specified by variance modifiers in
and out
(or Java wildcards).
Two cases are possible:
If you want only to take T
instances out of instances of your class A
, then you can use out
modifier: A<out T>
.
Here A<SubtypeB>
becomes a subtype of A<BaseB>
, because from A<SubtypeB>
you can obviously take instances of BaseB
, and not vice versa.
If you want only to pass T
into your class' methods, then use in
modifier in your class declaration: A<in T>
.
And here A<BaseB>
is a subtype of A<SubtypeB>
, because every instance of A<BaseB>
can also receive SubtypeB
into the methods, but not vice versa.
If you both pass and take T
to/from your class A<T>
, then the only option for T
it is to be invariant, so that neither A<SubB>
nor A<SuperB>
are subtypes of A<B>
: otherwise would lead to a contradiction to the above.
And this is exactly the case: in your BaseP<B>
, you are both taking items of V
and putting ones into view
property, so V
can only be invariant, and BaseP<SubF>
is not a subtype of BaseP<BaseF>
, neither is SubP<SubF>
.