Extension functions are not a replacement for all utility functions

When switching from Java to Kotlin many programmers discover a very convenient feature: an ability to extend existing classes through a mechanism known as “extension functions”.

It allows to simulate adding a new method to any class without inheriting from it and without touching source class in any way, with a result looking just as if an original class was modified:

import android.view.View
fun View.setVisible(visible: Boolean) {
this.visibility = if (visible) View.VISIBLE else View.GONE
// elsewhere:

However this mechanism often gets misused. Having the ability to naturally extend everything that was always closed feels so exciting at first that it is tempting to start converting code so it would use this new and shiny feature in many situations.

Like this one:

// Yay! I am a master of android's Context. Which is a God Object,
// so who am I then? You've guessed it! ;)
fun Context.loadImageCenterCrop(url: String, imageView: ImageView) {
// elsewhere...
context.loadImageCenterCrop("http://example.com/image.jpg", myImageView)

Or this one:

// A new way to show alert dialogs based on custom views
fun View.showAlertDialogAndGoBack(activity: Activity) {
.setPositiveButton("OK", null)
.setNegativeButton("Back", { activity.finish() })
// elsewhere in some Activity...

What’s wrong in these examples is that they are introducing extension methods which do not actually extend existing classes in any way. They are misusing this mechanism to simply shave off the first argument from some utility function, replace it with ‘this’ keyword inside a function definition and to syntactically move it earlier (before the dot) in the place of function invocation.

I am trying to say that those examples above are really just a good-old utility functions and that they should be written like ones instead:

fun loadImageCenterCrop(context: Context, imageView: ImageView, url: String) { /*...*/ }
fun showCustomAlertDialog(activity: Activity, customView: View) { /*...*/ }

Semantically these “bad” extension functions above are not extending anything, they are using those classes the same way they use their other arguments: as parameters to pass to some other function. Contrast these with an earlier example of extending a View with setVisible() method: this time we are clearly augmenting the View class with a new “nicer” way to switch between visible/gone states.

There is another hint on how to check if you are using extension functions correctly: if your implementation passes an instance of the type it extends (i.e. “this”) as an argument to some other function, it is very likely that you are writing an utility function, not an extension function. If, on the other hand, your implementation calls some function on “this” (like “this.setVisible()” for View), then it is very likely that you use an extension mechanism correctly.

Like what you read? Give Dmitry Suzdalev a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.