Have you ever wanted to add new functionality or attributes to a class’s API?
You can usually solve this problem by inheriting the class, or by creating a new function that takes an instance of the class as an argument. The Java programming language usually uses the Utils class to solve this problem, but this approach doesn’t support code auto-completion, making written code harder to find and less intuitive to use. Both approaches solve the problem, but it’s still hard to write clean, readable code.
Thankfully, Kotlin came to the rescue with extension functions and attributes. It lets you add functionality to a class without having to use inheritance or create functions that receive instances of the class. Unlike programming languages such as Java, Android Studio’s auto-completion feature supports the Kotlin extension. Extensions are available for third-party code libraries, the Android SDK, and user-defined classes.
Read on to discover how extensions can improve the readability of your code.
Extend the use of functions
Let’s say you have a class called Dog that has three attributes: name, breed, and age.
<! -- Copyright2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
data class Dog(val name: String, val breed: String, val age: Int)
Copy the code
Adoption agencies hope to expand the Dog category to include the ability to print information about a Dog, which could make it easier for interested people to adopt. To do this, we implement an extension function the same way we implement a normal function, except that you need to prefix the function name with the name of the class to be extended and a “.” sign. In the body of a function, you can use this to refer to the recipient object, which has access to all member objects of the recipient’s class within the scope of the function.
<! -- Copyright2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
fun Dog.printDogInformation(a) {
println("Meet ${this.name}, a ${this.age} year old ${this.breed}")}Copy the code
Calling the printDogInformation() method is just like calling any other function in the Dog class.
<! -- Copyright2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
fun main(a) {
val dog = Dog("Jen"."Pomeranian".13)
dog.printDogInformation()
}
Copy the code
Call extension functions from Java code
The extension function is not part of the class we are extending, so when we try to call this method in the Java language, we cannot find it in other methods of the class. As we will see later, the extension decompiles into static methods in the file it is defined and takes an instance of the class we want to extend as an argument. Here is sample code for calling the printDogInformation() extension function in Java.
<! Copyright 2019 Google LLC SPDX - License - Identifier: Apache - 2.0 - > DogExtensionKt. PrintDogInformation (dog);Copy the code
Define extension functions for nullable types
You can also define extension functions for nullable types. Instead of checking for null before calling the extension function, we can define extension functions for nullable types directly and have the extension function itself include null checks. Here is sample code to define the extension function printInformation() for nullable types.
<! Copyright 2019 Google LLC. Spdx-license-identifier: Apache-2.0 --> Fun Dog? .printInformation() { if (this == null){ println("No dog found") return } println("Meet ${this.name} a ${this.age} year old ${this.breed}") }Copy the code
As you can see, null checks are not required to call the printInformation() function.
<! -- Copyright 2019 Google LLC.spdx-license-Identifier: apache-2.0 --> Fun main() {val dog: dog? = null dog.printInformation() // prints "No dog found" }Copy the code
Use of extended attributes
As an adoption agency, you might also want to know if your dog is old enough to be adopted, so we implemented an extended property called isReadyToAdopt to check if your dog is older than 1 year.
<! Copyright 2019 Google LLC.spdx-license-Identifier: apache-2.0 --> val dog.isreadyToAdopt: Copyright 2019 Google LLC.spdx-license-Identifier: apache-2.0 --> Val dog.isreadyToAdopt: Boolean get() = this.age > 1Copy the code
Calling this property is like calling any other property in the Dog class.
<! -- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache --> fun main() {val dog1 = Dog("Jen", "Pomeranian", 13) if(dog1.isReadyToAdopt){ print("${dog1.name} is ready to be adopted") } }Copy the code
Overwrite in extension functions
You cannot override an existing member function of a class in an extension function. If the extension function you define is signed the same as an existing member function, only the existing member function will be called normally, because the function call depends on the static type at the time the variable is declared, not the runtime type of the value stored in that variable. For example, you can’t extend the toUppercase() method on String, but you can extend a method called convertToUppercase().
This behavior has consequences when you extend a type that is not part of your definition and that type exists in a code base with an extension function that has the same signature as your extension. In this case, the extension function in the code base is called, and the only information you get is that the extension function you defined becomes an unused method.
The working principle of
We can Decompile printDogInformation() in Android Studio by clicking the Decompile button in Tools/Kotlin/Show Kotlin Bytecode. Here is the code generated after decompiling printDogInformation() :
<! -- Copyright2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
public static final void printDogInformation(@NotNull Dog $this$printDogInformation) {
Intrinsics.checkParameterIsNotNull($this$printDogInformation, "$this$printDogInformation");
String var1 = "Meet " + $this$printDogInformation.getName() + ", a " + $this$printDogInformation.getAge() + " year old " + $this$printDogInformation.getBreed();
boolean var2 = false;
System.out.println(var1);
}
Copy the code
In fact, extension functions look like normal static functions that take an instance of a class as an argument and have no other connection to the receiving class. That’s why the code doesn’t have Backing Fields — they don’t actually insert any members into the class.
conclusion
Overall, extensions are a useful tool. Think carefully when using extensions, and keep these tips in mind to make your code more intuitive and readable.
Tip:
- Extensions are distributed statically;
- Member functions always “win”;
- Adopt a dog!
Have fun coding!