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 ofBaseA<B>
, so the problem is in the generics parameters subtyping.The answer lies in Kotlin generics variance, which is similar to that of Java.
The generics are invariant by default, which means that, even in simpler case, for a class
A<T>
,A<SubtypeB>
andA<BaseB>
are not subtypes of each other unless otherwise is specified by variance modifiersin
andout
(or Java wildcards).Two cases are possible:
If you want only to take
T
instances out of instances of your classA
, then you can useout
modifier:A<out T>
.Here
A<SubtypeB>
becomes a subtype ofA<BaseB>
, because fromA<SubtypeB>
you can obviously take instances ofBaseB
, and not vice versa.If you want only to pass
T
into your class' methods, then usein
modifier in your class declaration:A<in T>
.And here
A<BaseB>
is a subtype ofA<SubtypeB>
, because every instance ofA<BaseB>
can also receiveSubtypeB
into the methods, but not vice versa.If you both pass and take
T
to/from your classA<T>
, then the only option forT
it is to be invariant, so that neitherA<SubB>
norA<SuperB>
are subtypes ofA<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 ofV
and putting ones intoview
property, soV
can only be invariant, andBaseP<SubF>
is not a subtype ofBaseP<BaseF>
, neither isSubP<SubF>
.