I wrote 2 useful extensions in Kotlin

Tan Jun Rong
Android Programmer who likes writing blogs, reading, coffee, snowboarding.
Apr 2, 2018 615 words
Edit

Kotlin programming language has been gaining popularity since it first appeared in 2011. After it gained enough momentum and mature enough as a language, it was recognized as the official language for Android development in May 2017. The usage has since increased significantly!

The graph taken from StackOverflow by tag usage. πŸ‘‡
Screen Shot 2018-04-02 at 10.17.19 PM

One of the reason people love Kotlin language is because it offers many features for writing clean and readable code. One of it is the extension function. Extension function is a way to extend the capability of a class without sub-classing it.

Let's say we have a pet call pikachu, which is of type String and we want to add a method castSpell() to it.

kotlin

val name = "pikachu"

In Java-land, there are 2 ways of doing it, one way is to sub-class it:

sub-class.java

class MagicString extends String { public String castSpell() { return this + "β•°( ⁰ ਊ ⁰ )β”β˜†οΎŸ" } }

Or using a decorator pattern:

decorator-pattern.java

class MagicString { String original; public MagicString(String original) { this.original = original; } public String castSpell() { return original + "β•°( ⁰ ਊ ⁰ )β”β˜†οΎŸ" } }

By using the sub-classing method, it doesn't affect other normal string, so we will have to use MagicString in order to use castSpell() method.

By using decorator pattern, we need to access the orignal String through a level of indirection though: MagicString("pikachu").original.

Through extension function in Kotlin, we can just simply do this:

extension-function.kt

fun String.castSpell() { return this + "β•°( ⁰ ਊ ⁰ )β”β˜†οΎŸ" }

And we can use it like this πŸ‘‡

main.kt

val name = "pikachu" name.castSpell() // output: pikachuβ•°( ⁰ ਊ ⁰ )β”β˜†οΎŸ

With this feature, we can extend the capability of any objects, and it's very powerful.

Extending the Any class

In Java, all the classes is a sub-class of the main class Object. In Kotlin, the equivalent would be the Any class. So by extending the Any class, any method you insert can be used by any classes.

extending-Any-class.kt

fun Any.heyThere() { print("hey there!") }

now in the use side, any class will have the heyThere() method:

main.kt

99.heyThere() "temp".heyThere() // output: hey there! // output: hey there!

You might be wondering what methods would be useful for all classes to have. Some examples can be found in Standard.kt.

For example the .apply() method which is available for any objects, is useful for applying changes to an object.

apply-example

val person = Person().apply { name = "pikachu" }

There are also other methods like .apply(), such as: .let() .also() .run() .with(), which I talked in more detail in this post: How to use Kotlin's it also let apply run
.

letWith()

In kotlin, we finally have a way to deal with NullPointerException in a more elegant way.
When we have a nullable type String?, we can unwrap it by using one of the 4 methods also() let apply run, for example:

unwrap-nullable-let.kt

var name :String? = null name?.let { print(it) }

In some cases, I experienced the need to unwrap 2 nullable objects before performing an action, so I ended up doing:

main.kt

var firstName :String? = null var familyName :String? = null firstName?.let { _firstName -> familyName?.let { _familyName -> print("${firstName} ${familyName}") } }

It works, but it can be better, so I wrote an extension function called letWith():

letWith.kt

inline fun <T, T2, R> T?.letWith(secondArg: T2?, block: (T, T2) -> R): R? { return this?.let { secondArg?.let { block(this, secondArg) } } }

With this, now we can simplify it to this:

main.kt

firstName.letWith(familyName) { _firstName, _familyName -> print("${firstName} ${familyName}") }

No more nesting! πŸ˜€

ofType

I came across some situation where I only want to perform a certain task if the type is correct. For example:

Say we have an Animal class

Animal.kt

open class Animal

And we have a sub-class called Pikachu with extra method thunderShock():

Pikachu.kt

class Pikachu: Animal() { fun thunderShock() { print("zap zap!") } }

So we need to perform a type check, followed by a type cast like this:

main.kt

var target = Animal() target = Pikachu() if (pikachu is Animal) { val pikachu = (target as Pikachu) pikachu.thunderShock() }

So I wrote a function called ofType():

ofType.kt

inline fun <reified T> Any.ofType(block: (T) -> Unit) { if (this is T) { block(this as T) } }

By using ofType() function, it can now be simplified to this:

main.kt

var target = Animal() target = Pikachu() target.ofType<Pikachu> { it.thunderShock() }


by korderitto | DeviantArt

The name ofType is stolen from the Rx World. It is now shorter and more concise, and the dangerous casting is handled by the function. So as long as this method is tested, it is safer.

Do you have some extension functions that you kept hidden in your project? Share it with me! ❀️

That's all I have today, hope you enjoy the post!
Thanks for reading, see you next time! 🌻


Written By

Tan Jun Rong

Android Programmer who likes writing blogs, reading, coffee, snowboarding.

Enjoyed the post?

Clap to support the author, help others find it, and make your opinion count.

Comments

To leave a comment, you need to login first πŸ˜‰