130

Programmer dictionary: Extension receiver vs Dispatch receiver

 6 years ago
source link: https://blog.kotlin-academy.com/programmer-dictionary-extension-receiver-vs-dispatch-receiver-cd154e57e277?gi=1b29a023de65
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Programmer dictionary: Extension receiver vs Dispatch receiver

The concept of receivers was previously explained, so make sure you know what receiver is before reading further.

1*RBntJ9Ivhd4ZW4K-qQSjPQ.jpeg

Extension receiver is the receiver that is closely related to Kotlin extensions. Extension receiver represents an object that we define an extension for. To define extension we must precede it with receiver type (usually name of the class or interface). Extension receiver is an instance of the object that extension is invoked on. Let’s look at Ball.bounce extension function:

fun String.firstChar(): Char = this.get(this.length — 1)fun String.lastCharacter() {
println(“Receiver type is ${this.javaClass}, Reciver object is ${this.name}”)
}val ball = Ball(“Golf ball”)
ball.bounce()
// prints: Receiver type is Ball, Receiver object is Golf ball

The above instance of Ball class is accessible as extension receiver inside the body of bounce extension. Receiver type specifies the type of receiver object (Ball), while Receiver object is an instance of the class that is a receiver (Golf ball / Tennis ball). Notice that in case of extensions receiver object behaves as any other object, so we can’t access members with private or protected visibility.

Dispatch receiver is a special kind of receiver existing when the extension is declared a member. It represents an instance of the class in which the extension is declared in. Imagine that we will define uploadToBackend extension for Person class. Notice that we will define this receiver inside Network class:

class Person {
fun move() {}
}class NetworkRepository {
fun loadData() {} fun Person.uploadToBackend() {
move() //method from extension receiver
loadData() //method from extension dispatch receiver
}
}

In the body of a uploadToBackend extension method, we can call methods from Person class and NetworkRepository class. The reason behind this, is that our uploadToBackend method has two receivers. First is extension receiver that represents an instance of the class on which extension was invoked. Second is dispatch receiver representing an instance of the class where the extension is declared. Notice that both of the receivers are implicit receivers in above example — we are calling two different methods on two different objects without specifying a receiver.

To call our extension method for Person class we need extension receiver of Database type.

class Person {
fun move() {}
}class Database (al person:Person) {
fun loadData() {} fun doSomething() {
person.uploadToBackend(); // We can access extension here
}

fun Person.uploadToBackend() {
move()
loadData()
}
}val person = Person()
person.uploadToBackend() // Compilation error

Compiler will report error when we call uploadToBacnekd method outside Database class (in context that does not have receiver of Database type). We can call this extension inside Database class because proper receiver is available.

But what happens if both methods would have the same signature? Let’s add move method to Database class.

class Person {
fun move() {}
}class NetworkRepository (val person:Person) {
fun loadData() {}
fun move() {} fun doSomething() {
person.uploadToBackend();
} fun Person.uploadToBackend() {
// calls method defined in Person class
move() // calls method defined in NetworkRepository class
[email protected]()
}
}

By default move method defined in Person class will take precedence, but if we want to access method in NetworkRepository we need to use explicit receiver (this@NetworkRepository).


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK