Home > Net >  How does the dispatch of extensions declared as members relate to receiver type in this example?
How does the dispatch of extensions declared as members relate to receiver type in this example?

Time:12-27

I've read this answer but I'm still confused. When looking at this example in the docs...

open class Base { }

class Derived : Base() { }

open class BaseCaller {
    open fun Base.printFunctionInfo() {
        println("Base extension function in BaseCaller")
    }

    open fun Derived.printFunctionInfo() {
        println("Derived extension function in BaseCaller")
    }

    fun call(b: Base) {
        b.printFunctionInfo()   // call the extension function
    }
}

class DerivedCaller: BaseCaller() {
    override fun Base.printFunctionInfo() {
        println("Base extension function in DerivedCaller")
    }

    override fun Derived.printFunctionInfo() {
        println("Derived extension function in DerivedCaller")
    }
}

fun main() {
    BaseCaller().call(Base())   // "Base extension function in BaseCaller"
    DerivedCaller().call(Base())  // "Base extension function in DerivedCaller" - dispatch receiver is resolved virtually
    DerivedCaller().call(Derived())  // "Base extension function in DerivedCaller" - extension receiver is resolved statically
}

And regarding this quote

An instance of a class in which the extension is declared is called a dispatch receiver, and an instance of the receiver type of the extension method is called an extension receiver.

I assume that only instances of Base() and Derived() can be extension receivers, because we only defined extensions for those two classes. And I assume that only instances of BaseCaller() and DerivedCaller() can be dispatch receivers, because we only declare extensions inside those classes.

My confusion stems from the two comments at the bottom of the example where it says "dispatch receiver is resolved virtually" and "extension receiver is resolved statically". Are they referring to DerivedCaller() in both cases? Is DerivedCaller() a dispatch receiver in one line and an extension receiver in the next? How can it be an extension receiver if we never declare any extensions for it? Or are they referring to Base() as the dispatch receiver and Derived() as the extension receiver? How can Base() be a dispatch receiver, if it never declares any extensions?

I know that call() always resolves statically to use an instance of Base, and I know the version of Base.printFunctionInfo() will depend on what object I call the call() method on (either BaseCaller() or DerivedCaller()), but I'm confused about what those comments are saying. Thanks in advance!

CodePudding user response:

I was originally confused by these two comments from the docs

  1. "dispatch receiver is resolved virtually"
  2. "extension receiver is resolved statically"

In the first comment, the "dispatch receiver" refers to the DerivedCaller() instance. It's virtual because they could've called the call() method on a BaseCaller() instance or a DerivedCaller() instance (but they ended up calling it on a DerivedCaller() instance). This first comment is concerned with which extension method to call, the one in BaseCaller or the one in DerivedCaller

In the second comment the "extension receiver" refers to the Derived() instance that gets passed into call(). This second comment is concerned with which member function get's called from inside the DerivedCaller class. The Base.printFunctionInfo() or the Derived.printFunctionInfo(). In this case the extension receiver is resolved statically, so instead of using Derived() it uses Base() instead because that's how my extension function is setup (and extension functions are always resolved statically).

CodePudding user response:

This is much easier to understand if we realize that Base.printFunctionInfo() and Derived.printFunctionInfo() are much different functions, only named the same. If we rename them, this example becomes obviously clear:

open class BaseCaller {
    open fun Base.printFunctionInfoForBase() {
        println("Base extension function in BaseCaller")
    }

    open fun Derived.printFunctionInfoForDerived() {
        println("Derived extension function in BaseCaller")
    }

    fun call(b: Base) {
        b.printFunctionInfoForBase()   // call the extension function
    }
}

class DerivedCaller: BaseCaller() {
    override fun Base.printFunctionInfoForBase() {
        println("Base extension function in DerivedCaller")
    }

    override fun Derived.printFunctionInfoForDerived() {
        println("Derived extension function in DerivedCaller")
    }
}

In call() we only have a Base object, so we can only invoke printFunctionInfoForBase(), but we can't invoke printFunctionInfoForDerived() there. Then, we clearly see that printFunctionInfoForDerived() is really never invoked anywhere in this code.

In other words, Base.printFunctionInfo() and Derived.printFunctionInfo() are two overloads of the function. DerivedCaller provides overrides of BaseCaller functions.

  • Related