Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

kotlin generics in KFunction1

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

like image 503
alexadr Avatar asked Oct 31 '25 04:10

alexadr


1 Answers

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
like image 50
hotkey Avatar answered Nov 03 '25 01:11

hotkey



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!