Suppose, you have two classes TestA and TestB. Suppose that TestA extends TestB:
public class TestB {
private int intProp;
public int getIntProp() {
return intProp;
}
public void setIntProp(int intProp) {
this.intProp = intProp;
}
}
public class TestA extends TestB {
private String strProp;
public String getStrProp() {
return strProp;
}
public void setStrProp(String strProp) {
this.strProp = strProp;
}
}
Now I create next line of code:
var getter1: KFunction1<TestA, Int> = TestA::getIntProp
As you can see, I access from TestA class method of TestB: TestA::getIntProp SO the result is instance of KFunction1 with generic params < TestA, Int >
Now I try to create next line of code
var getter2: KFunction1<TestA, Int> = TestB::getIntProp
And it also works and compiles, while I expect that there will be compile error
This is generics variance in Kotlin, its purpose is to impose type-safe hierarchy on generic classes with parameters which are in class hierarchy themselves.
Class KFunction1 has generic parameters defined as <in P1, out R>, P1 with in modifier and R with out modifier. This means that there will be:
Contravariance on P1 introduced by in modifier.
Any KFunction1<PSuper, R> will be a subtype of KFunction1<P, R> if PSuper is a supertype of P. But the limitation will be added that P1 can only appear as argument of (passed into) KFunction1's members.
Covariance on R introduced by out modifier.
Any KFunction1<P, RSub> will be a subtype of KFunction1<P, R> if RSub is a subtype of R. And here R will be limited: it can only be used as a return value (passed out) of KFunction1's members.
Therefore you will be able to assign KFunction1<PSuper, RSub> to a variable of type KFunction1<P, R>.
This makes sense for KFunction1, because any function that receives an argument of type PSuper can also receive an instance of P (but not vice versa), and any function that returns instances of RSub in the same time returns instances of R (but not vice versa).
You can simply compose an example which shows this behaviour:
class Invariant<T> {
fun f(i: Int): T { throw Exception() } // OK
fun f(t: T): Int { throw Exception() } // OK
}
class Contravariant<in T> {
fun f(i: Int): T { throw Exception() } // error, T cannot be returned
fun f(t: T): Int { throw Exception() } // OK, T is parameter type
}
class Covariant<out T> {
fun f(i: Int): T { throw Exception() } // OK, T is returned
fun f(t: T): Int { throw Exception() } // error, T cannnot be parameter type
}
open class Base
class Derived: Base()
val i1: Invariant<Base> = Invariant<Derived>() // error
val i2: Invariant<Derived> = Invariant<Base>() // error
val n1: Contravariant<Base> = Contravariant<Derived>() // error
val n2: Contravariant<Derived> = Contravariant<Base>() // OK
val v1: Covariant<Base> = Covariant<Derived>() // OK
val v2: Covariant<Derived> = Covariant<Base>() // error
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With