Thursday, June 28, 2018

Kotlin - Great thing about Extension Functions.

Before start Kotlin Extension Functions, let's recall inheritance in Java. Suppose, you need to extend a class with new functionality. In most programming languages, you either derive a new class or use some kind of design pattern to do this. I have created a subclass and added extra methods as you can see below. 
Java Inheritance
public class CircularImageView extends ImageView { public CircularImageView (Context context) { super(context); }
public void loadProfilePic(String url) { // Download the image } }

There are a few problems with this approach:

1. Composition over inheritance is a principle in object-oriented programming languages. Its main idea is that classes should achieve polymorphic behavior and code reuse by containing instances of other classes rather than inheriting from a parent class. In Java, there is no multiple inheritances (nor in Kotlin) which protects us programmers from making a deadlock, but it also makes hard to e.g. mix the functionality of CircularImageView with ZoomImageView into one AwesomeImageView class.

2. Using custom types instead of what the Android SDK provides is sometimes inconvenient. You have to write more code to make the place for subclasses instead of just using the regular ImageView. You and everyone working on a project have to keep in mind what kind of special class fits the current requirement.

Let's come back and start the main topic.

Kotlin Extension Functions
Kotlin has to remove the above problem with the help of extension functions. The extension functions help us to extend the functionality of classes without having to touch their code.

The extension functions in Kotlin allow us to extend the functionality of a class by adding new functions. The class doesn’t have to belongs to us (could it be a third party library) and also without requiring us to inherit the class. With this feature, you can create new methods to an existing type without introducing inheritance.

Although, it's important to say that you can only access public methods and fields (because extensions are resolved statically) when making shortcuts and designing new interfaces to interact with the SDK, it really makes a difference.

Let's convert the above code in Kotlin extension functions.
Example - 1
fun ImageView.loadProfilePic(url: String) { Glide.with(context).load(url).into(this) }
//Now we can use like this imageView.loadProfilePic(url)
That's it. Move this file where you defined this function from project to project or create a library with a collection of other helper functions. After that, every simple ImageView will be capable of downloading an image.

How kotlin extension functions work.
The extension functions are nothing but regular static functions which takes an instance of the receiver class as a parameter implicitly when you define the function and operates on that. This is the reason why you have access to all the members of the class inside your function block. They do not have any connection the receiver classes in any other form.

Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on variables of this type.

We would like to emphasize that extension functions are dispatched statically. This means that the extension function is called is determined by the type of the expression on which the function is invoked, not by the type of the result of evaluating that expression at run-time.
Example - 2
//Create a vehicle class open class Vehicle{ } //Create bike class and extend vehicle class class Bike: Vehicle(){ } //Set names fun Vehicle.name() = "vehicle" fun Bike.name() = "bike" fun printName(vehicle: Vehicle) { println(vehicle.name()) } //call printName(Bike())
This example will print "vehicle ", because the extension function being called depends only on the declared type of the parameter vehicle, which is the Vehicle class.

If a class has a member function and an extension function is defined which has the same receiver type, the same name and is applicable to given arguments, the member always wins.
Example - 3
class Vehicle { fun name() { println("member") } } fun vehicle.name() { println("extension") }

If we call vehicle.name() of any vehicle of type Vehicle, it will print member, not an extension.

However, it's perfectly OK for extension functions to overload member functions which have the same name but a different signature:
Example - 4
class Vehicle {
fun show() { println("member") } } fun vehicle.show(i: Int) { println("extension") }
The call to Vehicle().show(1) will print "extension".

Nullable Receiver
Extensions can be defined with a nullable receiver type. Such extensions can be called on an object variable even if its value is null and can check for this == null inside the body. This is what allows you to call toString() in Kotlin without checking for null. The check happens inside the extension function.
Example - 5
fun Any?.toString(): String { if (this == null) return "null" // after the null check, 'this' is autocast to a non-null type, so the toString() below // resolves to the member function of the Any class return toString() }
Companion Object Extensions.
Kotlin introduces the concept of a companion object which essentially replaces Java’s static members. A companion object is a singleton object that belongs to the class itself, rather than an instance of the class. It contains the variables and methods that you might want to access in a static fashion.

You can create a companion object by adding the companion keyword to the object declaration inside the class.
Example - 6
class myClass { companion object { } }
If a class has a companion object defined, then you can add a static extension function to this class, by inserting “companion” between the extension type and the function name.
Example - 7
Class MyClass
companion object { } } fun MyClass.Companion.showDomain() { println("www.developerlibs.com") } }
Here, we’re defining the extension function showDomain on the companion object MyClass.Companion. Similarly to the other extension function variants we’ve looked at, you’re not actually modifying the class. Instead, you’re adding the companion object extension to the companion object.

Once you’ve defined a companion object extension, you can call the extension function as though it’s a regular static function defined inside the ‘myClass’ companion object:
Example  - 8
MyClass.showDomain()
Note that you’re calling this extension using class type, not a class instance.

Member functions always win.
If you want to define an extension function which has the same signature. A member function that presents inside the receiver class, the member function is executed when we make a call to the function.
Example - 9
class Domain{ //Declaring an empty companion object her companion object fun showMe() { println("www.developerlibs.com") } }
If we try to add an extension function showMe() with a different implementation to our class.
Example - 10
fun Domain.showMe() { println("Kotlin tutorial") }
Now, if you call our function we will get the output of the member function and not the newly added extension function. The scenario kind of looks like this,

Output: www.developerlibs.com

As you can see clearly from the output that when we made the call, Kotlin chose the member function to execute and not the extension function.


There are many places in Android Development, where we can use this cool feature of Kotlin. Let’s use this great feature whenever it is required.



                                       Previous                                             Next                                                                                        


Share:

Get it on Google Play

React Native - Start Development with Typescript

React Native is a popular framework for building mobile apps for both Android and iOS. It allows developers to write JavaScript code that ca...