loading...

Kotlin: What are your favorite extension functions that you authored?

vishnuharidas profile image Vishnu Haridas Updated on ・2 min read

Also published on my blog here: https://iamvishnu.com/posts/kotlin-extension-functions

Kotlin has this nice feature called "Extension Functions" β€” adding new functionality to an existing class, without inheritance or writing lots of boilerplate code. Extension functions and extension properties are allowed in Kotlin.

Here is an example Extension for the Float class by adding a new function called asDollars() to print that float number as a currency value:

fun Float?.asDollars() = "USD%.2f".format(this ?: 0.0F)

Now anywhere in code I can write:

val money = 42.0F
println( money.asDollars() ) // will print "USD42.00"

Note: An extension function doesn't really modify the existing class. Instead, the receiver object is passed as the first parameter during compile time. For more, read: Extension functions are resolved statically.

Here are some of my own Extensions that I use across many projects in Android:

1. Some AlertDialogs

fun Context.showAlertDialog(
    title: String,
    message: String,
    ok: Pair<String,()->Unit>,
    cancel: Pair<String,()->Unit>? = null){

    val builder = AlertDialog.Builder(this)
        .setTitle(title)
        .setMessage(message)
        .setCancelable(false)
        .setPositiveButton(ok.first) { _,_ -> ok.second() }

    cancel?.let{
        builder.setNegativeButton(it.first) { _, _ -> it.second() }
    }

    builder.create().show()
}

fun Context.showConfirmDialog(
    title: String,
    message: String,
    ok: Pair<String,()->Unit>,
    cancel: Pair<String,()->Unit>? = null
) = showAlertDialog(title, message, ok, cancel)

fun Context.showErrorDialog(message: String, action: () -> Unit){
    showAlertDialog(
        title = "Error",
        message = message,
        ok = "OK" to action
    )
}

Usage on Activity (or Fragments):

fun onResume(){
    showAlertDialog(
        title = "Welcome",
        message = "Hello, welcome to the show! Do you want to proceed?",
        ok = "Yes" to { nextScreen() },
        cancel = "No" to { finish() }
    )

    // showing an error
    showError("No internet connection. Please try later") { finish() }
}

2. A Simple Click Listener

inline fun View.onClick(crossinline f: ()-> Unit) = this.setOnClickListener { f() }

Usage:

btnNext.onClick { nextScreen() }

3. Some EditText validations

fun isValidText(s: String?) = s!=null && !s.isEmpty()
fun isValidEmail(s: String?) = isValidText(s) &&  android.util.Patterns.EMAIL_ADDRESS.matcher(s).matches()

fun EditText.trimmedText() = this.text?.trim().toString()
fun EditText.isValidText() = isValidText(this.trimmedText())
fun EditText.isValidEmail() = isValidEmail(this.trimmedText())

Usage:

isValidForm = editEmail.isValidEmail() && editUsername.isValidText()

What are your favorite Extension Functions / Properties that you have authored for your projects?

Posted on Jan 1 '19 by:

Discussion

markdown guide
 

I really liked the onClick extension. But I slightly disagree with some of the extensions. Kotlin extensions are a way of implementing the Open Closed Principle. Classes remain closed for modification and open for extension. But are the extension really a responsibility of the class? For example, Float.asDollar(), does float class should be aware of anything related to currencies. In my opinion Float class should be exposing functions only related to decimal point numbers, not really converting it to a very specific string format. Similar is my opinion about Context.showAlertDialog. While we require the help of Context to build an alert dialog, showing it is might not be the responsibility of Context. What do you think?

 

Extension functions never extend a class, rather they are resolved statically. No new classes are created. Instead, during compile-time, they just create a function with the receiver object as the first parameter of that class. I would say, Extensions are just syntactic sugar for utility functions in Kotlin.

For example, Float.asDollar() compiles into:

fun asDollar(receiver: Float): String {
    return "USD%.2f".format(receiver ?: 0.0F)
}

...and when we make a call to print(123.0F.asDollars()) this happens:

   print(asDollars(123.0F))

So Extension Functions are nothing more than just those utility functions that we used to keep inside the utils.* package or Utils.java class previously.

In my opinion, extensions are actually good at making a function discoverable and keeping everything under a single namespace.

 

Kotlin provides the ability to extend a class with new functionality without having to inherit from the class or use design patterns such as DecoratorThis is the very first line of Kotlin extension function documentation. As it states Kotlin extensions are a way to extend a class with new functionality. I agree that there is no actual extension happening and it is resolved statically. But those are implementation details in my opinion. If you look at the examples the doc has, first one is adding a swap functionality to a MutableList class. Swap should have been a responsibility of MutableList class but it is missing. Extension enable us to add such missing functionalities to classes. I really like to think extensions and utility methods are two different entities.

Extension enable us to add such missing functionalities to classes.

Really I don't see such restriction anywhere! I understand that we are free to add new functionality to any class even if the function is not an actual responsibility of the receiver type. Am I missing something there?

There is no such limitation documented anywhere, you can add new functionality to any class even if it is not related, is because it is not possible to figure out whether functionality belong to the class or not. Consider how you use the extension function to understand what I was trying to say. Say you added a method show toast to String class.

fun String.showToast(context: Context){
     Toast.makeText(context, this, LENGTH_SHORT).show()
}

When you call it from any class it will be something like this

val name = user.name
name.showToast(context)

It has become like name.length() or name.isEmpty(). But the added methods should not be really part of String class. Look at the extensions provided by android-ktx library. It can be found that those extensions belong to specific classes and and are not very generic utility methods