In continuation to my last Medium post Extensions in Kotlin where I explained what are Extensions and how do we use it, this medium post will cover the implementation of Extensions as members of some other class.
An extension can be defined as members of some other class also.
The benefit of defining an extension as a member of the other class is, we can access all the functions and properties of both the classes inside that extension method.
Let’s take an example
class BaseClass {
fun baseClassFunctionality() {
}
}
class OtherClass {
fun otherClassFunctionality() {
}
fun BaseClass.additionalFunctionality() {
baseClassFunctionality()
otherClassFunctionality()
}
}
We have two classes named BaseClass
and OtherClass
. We have a method called baseClassFunctionality
in BaseClass
and otherClassFunctionality
in OtherClass
. Now, we added an extension function of BaseClass in OtherClass naming additionalFunctionality. The benefit of adding an extension function of BaseClass
in OtherClass
is we can access all the methods of BaseClass
and OtherClass
in that method.
What if both the classes have some same method with the same signature?
It will call the method of the class for which the extension function is defined. But if we want to call the method of the class in which the extension function is defined, we can use the qualified this expression.
class BaseClass {
fun printString() {
println("I am in BaseClass")
}
}
class OtherClass {
fun otherClassFunctionality() {
BaseClass().additionalFunctionality()
}
fun printString() {
println("I am in OtherClass")
}
fun BaseClass.additionalFunctionality() {
printString()
this@OtherClass.printString()
}
}
// the output of
OtherClass().otherClassFunctionality()
//will be
I am in BaseClass
Extension function as members can also be declared as open and can be overridden in the derived class. Which function will be called will be decided at runtime for virtual methods but statically for extension methods.
Let me explain this with an example.
Let’s say we have a class named BaseClass
and another class named DerivedClass
which is extending the BaseClass
.
Now we have another class called OtherClass
having two open extension functions of both BaseClass
and DerivedClass
(one each). Also, we have one more class extending the OtherClass
and overriding both the extension methods.
open class BaseClass {
}
class DerivedClass : BaseClass() {
}
open class OtherClass {
open fun BaseClass.someFunctionality() {
println("BaseClass.someFunctionality in OtherClass")
}
open fun DerivedClass.someFunctionality() {
println("DerivedClass.someFunctionality in OtherClass")
}
fun caller(baseClass: BaseClass) {
baseClass.someFunctionality()
}
}
class DerivedOtherClass : OtherClass() {
override open fun BaseClass.someFunctionality() {
println("BaseClass.someFunctionality in DerivedOtherClass")
}
override fun DerivedClass.someFunctionality() {
println("DerivedClass.someFunctionality in
DerivedOtherClass")
}
}
Now will consider four different cases which will clear the picture of overriding extension methods.
OtherClass().caller(BaseClass())
DerivedOtherClass().caller(BaseClass())
OtherClass().caller(DerivedClass())
DerivedOtherClass().caller(DerivedClass())
- When we call the caller method of
OtherClass
withOtherClass
reference object and pass theBaseClass
object as the parameter.
OtherClass().caller(BaseClass())
//the output of the above code will be
BaseClass.someFunctionality in OtherClass
This will call the extension function of BaseClass
defined in OtherClass
. There was no confusion because both the references were of the superclasses.
- When we call the caller method of
OtherClass
withDerivedOtherClass
reference object and pass theBaseClass
object as the parameter.
DerivedOtherClass().caller(BaseClass())
//the output of the above code will be
BaseClass.someFunctionality in DerivedOtherClass
This will call the extension function of BaseClass
overridden in DerivedOtherClass
.
The virtual dependency of OtherClass
and DerivedOtherClass
was resolved at runtime and it called the DerivedOtherClass’s
overridden method.
- When we call the caller method of
OtherClass
withOtherClass
reference object and pass theDerivedClass
object as the parameter.
OtherClass().caller(DerivedClass())
//the output of the above code will be
BaseClass.someFunctionality in OtherClass
This will call the extension function of BaseClass
defined in OtherClass
.
Even though we passed the DerivedClass
object as the parameter, it called the BaseClass
method only because the extensions are resolved statically.
In the caller method, we have defined the parameter type as BaseClass
so it will always call BaseClass
extension methods only.
- When we call the caller method of
OtherClass
withDerivedOtherClass
reference object and pass theDerivedClass
object as the parameter.
DerivedOtherClass().caller(DerivedClass())
//the output of the above code will be
BaseClass.someFunctionality in DerivedOtherClass
This will call the extension function of BaseClass
overridden in DerivedOtherClass
.
The reason is the same.
The virtual dependency of OtherClass
and DerivedOtherClass
was resolved at runtime and it called the DerivedOtherClass’s
overridden method and because the extensions are resolved statically, it called the BaseClass
extension method only.
Also, extension utilizes the same visibility of other entities as regular functions declared in the same scope would.
-
If the extension is defined at the top level of the class, it can access all the private variables and functions of that class.
-
If the extension function is defined outside the class, it can not access the private variables or functions of that class.