I wrote 2 useful extensions in Kotlin
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. π
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.
val name = "pikachu"
In Java-land, there are 2 ways of doing it, one way is to sub-class it:
class MagicString extends String {
public String castSpell() {
return this + "β°( β° ΰ¨ β° )ββοΎ"
}
}
Or using a decorator pattern:
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:
fun String.castSpell() {
return this + "β°( β° ΰ¨ β° )ββοΎ"
}
And we can use it like this π
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.
fun Any.heyThere() {
print("hey there!")
}
now in the use side, any class will have the heyThere()
method:
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.
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:
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:
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()
:
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:
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
open class Animal
And we have a sub-class called Pikachu
with extra method thunderShock()
:
class Pikachu: Animal() {
fun thunderShock() {
print("zap zap!")
}
}
So we need to perform a type check
, followed by a type cast
like this:
var target = Animal()
target = Pikachu()
if (pikachu is Animal) {
val pikachu = (target as Pikachu)
pikachu.thunderShock()
}
So I wrote a function called ofType()
:
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:
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! π»
Tan Jun Rong
Clap to support the author, help others find it, and make your opinion count.