Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin: override generic property within subtype

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?

like image 251
Alex Bubnov Avatar asked Feb 16 '16 11:02

Alex Bubnov


1 Answers

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>.

like image 102
hotkey Avatar answered Oct 18 '22 20:10

hotkey