Background photo by Christian Perner on Unsplash

Kotlin Fever : Part 2 — Functions and Expressions

Filip Babic
Published in
8 min readAug 28, 2017

--

I admit, it took me a while, but I’ve finally found some time to continue the series about awesome Kotlin features! This time the fever is all about featuring expressivity when it comes to variables, Global functions and Extension functions.

Everything is an Expression

Kotlin supporting both object oriented and functional style of programming might not be some news, but that does make it quite a diverse language in the Android development world.

It also allows us to use well known language features as functions/expressions such as the if/else statement, a try/catch block and even a when statement (switch on steroids). The last statement in aforementioned constructs is what’s assigned to the variable, which sometimes looks a bit weird, if you’re encountering these kinds of expressions for the first time.

An example of using a try block to assign a value to a variable:

val fileToUpload = try {
File(imagePath)
} catch (error: FileNotFoundException) {
null
}

fileToUpload?.let { uploadImage(it) }

Instead of the usual procedure: declaring a property first, setting it inside a try block and then not knowing whether or not it’s been initialized, we could simply assign the result of a try/catch expression to a variable. Here we can use a val and, knowing that our fieldToUpload is a value (immutable), proceed to upload it in a thread safe way, and in a null-pointer free environment.

This snippet would try to retrieve an image file from the specified path, and if that file exists proceed to upload it, quite a common scenario in Android.

Let’s see about more examples… We’re all familiar with the ternary operator, but it does have its limits. Mostly the part where, if more operations are needed to get the end result, you’d have to declare a function to provide one of the assignment options. In Kotlin you can just write:

val productPrice: Double = if (isDiscountActive) {
val totalDiscountPercent = calculateBonusPointsDiscount(discountAmount, bonusPoints)

val priceAfterCoupon = if (hasCoupon) currentPrice - 30 else currentPrice

priceAfterCoupon - priceAfterCoupon * totalDiscountPercent
} else {
currentPrice
}

The if statement’s block’s result is assigned to a value productPrice. See how we can first calculate the totalDiscount, then the price after coupons, before taking off the discount. Also notice how the priceAfterCoupon can be written in a single line, just like a ternary operator, but more expressive.

We could also do this for functions. A function can return by simply stating it with the assignment operator :

fun discount(price: Double, discount: Double) = price * discount

Note that even though you don’t have to state which return type you expect, or what type a value is when using expressions, I strongly advise you to do so. Sometimes the compiler just sets it as Any, because it cannot clearly see what you’re trying to do, and you could have a bug in your code.

Since we can use an expression as a return value to a function, we can do something like this:

fun getPersonHeight(height: Int): PersonHeight = if (height in 150..170) {
PersonHeight.SHORT
} else if (height in 171..180) {
PersonHeight.MEDIUM
} else if (height in 181..200) {
PersonHeight.TALL
} else {
PersonHeight.OTHER
}

When?

When has already been mentioned here as a super-switch statement, and an expression. Although it’s sometimes overlooked, there is actually great potential for a when statement to clean up your code, and reduce the number of lines required to handle different error cases.

Checking out our previous example with heights, it seems kind of annoying to write all those else if statements, more so if we just randomly insert checks without any sense of flow. Having a when expression instead of an if/else comes down to only a few simple lines of code:

fun getPersonHeight(height: Int): PersonHeight = when (height) {
in 150..170 -> PersonHeight.SHORT
in 171..180 -> PersonHeight.MEDIUM
in 181..200 -> PersonHeight.TALL
else -> PersonHeight.OTHER
}

This is easier to understand than a bunch of if/else statements, also it’s more functional in approach, as this is a pure function, it just takes something, evaluates it and returns something else, without having any side effects.

Even with a short problem like this, we can see the benefits of combining Kotlin’s features. I have one more example for you which I personally came across during my time playing with Kotlin goodies.

Say you have a registration screen, and of course in some situations you have to show general errors like “Fields are empty!”, but in others you have to show specific ones like “E-mail pattern is invalid.”. Instantly a large if/else statement comes to mind. Convenient, yes, but there is a better way, a more Kotlin way. We’ll be looking at how this can be done in Java, and a better way to do it in Kotlin:

public void checkUserCredentials(RegisterRequest credentials) {
if (credentials != null) {

if (!StringUtils.isValid(credentials.getUsername(),
credentials.getEmail(),
credentials.getPassword(),
credentials.getRepeatPassword())) {
showEmptyCredentialsError(credentials);
} else if (!StringUtils.isValidEmail(credentials.getEmail())) {
registrationView.showInvalidEmailError();
} else if (!StringUtils.isValidPassword(credentials.getPassword())) {
registrationView.showPasswordLengthError();
} else if (!StringUtils.match(credentials.getPassword(), credentials.getRepeatPassword())) {
registrationView.showPasswordMatchingError();
} else {
registerUser(credentials);
}
}
}

Quite a cognitive load right? A lot of stuff going on here, lots of words and lots of code, long expressions, and a few other things that bother me. First of all, we have a nullcheck, very Javaish. Then the repetitive credentials.something() calls making the if case very long. And of course icing on the cake: the if/else statements themselves, which are hard to expand with a new case.

fun checkUserCredentials(credentials: RegisterRequest) = with(credentials) {
when {
!listOf(username, email, password, repeatPassword).isValid() -> showEmptyCredentialsError(credentials)

!email.isValidEmail() -> view.showInvalidEmailError()

!password.isValidPassword() -> view.showPasswordLengthError()

!match(password, repeatPassword) -> view.showPasswordMatchingError()

else -> registerUser(credentials) //all is well, proceed
}
}

Feels much better, doesn’t it? There are no null checks here, there aren’t any issues with adding an additional case. We just start writing it, there is no else-if block to be inserted.

More so, with the help of some extension and global functions for our validations and with to not have to call credentials.field all the time, we’re able to reduce the code to 8 expressive lines.

We’ll be covering global and extension functions in a second! I’ll set aside Lambdas and higher order functions for another article as it’s a bigger topic.

Extension functions vs. Global functions

Ever had an Android API, or even Java, that is just terrible to work with? Or maybe a task you’ve repeated so many times that it has deserved its own place in SomethingUtils? Rather than constantly creating tons of files and static Utility methods, there are two ways of doing it in Kotlin that are better in my book.

The first one is using Extension functions, a concept of “adding content” to existing and new classes. By creating an extension function for a type, you give every object of the said Type the ability to call that method (or value).

fun Int.percent(): Double = (this / 100).toDouble()
val magicNumber = 42

magicNumber.percent() // 0.42

This very simple function allows you to turn any Int into a percentage! However, it may not be the most useful thing in Android, so we should look at something more useful in our everyday struggles.

So, you’ve written tons of AlertDialogs, and you know the annoyance of adding buttons to it. With a simple extensions you can beautify this process a bit :

inline fun AlertDialog.Builder.negativeAction(message: String, action: () -> Unit): AlertDialog.Builder {
setNegativeButton(message, { dialog: DialogInterface, _: Int ->
action()
dialog.dismiss()
})

return this
}

Calling this would look like :

builder.negativeAction(message = "Cancel", action = {
//do some stuff
})

Instead of a horrible listener being implemented at the call site, you take out that part of the boilerplate and separate it for general use. See how we made the method return a AlertDialog.Builder? This allows us to chain method calls, so you can add a Neutral button and a Positive button as well.

Global functions

So right now you’re feeling: “I’m going to add an extension to everything!”. A very common feeling, and as everyone who’s shown extension functions to anyone, I’ll just say this : Don’t. One common phrase says, when you’re equipped with a hammer everything starts to look like a nail.

So while you might feel that this seems like a good idea:

fun Any.log(key : String) {
Log.d(key, this.toString())
}
{ ...
number.log(key = "Logging") //usage
}

allowing you to log virtually anything easily, it’s really not. What it should be is a utility function instead. Now static is kind of weird in Kotlin, to the point that there is none, so utility in its usual nature is not what I meant. What I was going for was a global helper function! Kotlin supports top level declarations, meaning you can have a function, standalone, classless. By importing it (yeah just like you do with classes) you get the possibility of calling such a function. Enough talk, here’s the code:

fun log(what: Any, key: String) {
Log.d(key, what.toString())
}
{ ...
log(number, "Logging")
}

This is much better, we don’t bind any receiver at all to it causing confusing code (I myself would be a bit confused seeing a log function being used on a number), and it’s clean to use, notice there is no SomeUtils.someMethod() call anymore, you just call the function!

A good start would be to sit down and think over what’s really using its receiver and what is not. Some times you may not see the difference, and that’s okay, so this one is kind of confusing:

fun loadImage(target: ImageView, imagePath: String) {
Glide.with(target.context).load(imagePath).into(target)
}

fun ImageView.loadImage(imagePath: String) {
Glide.with(context).load(imagePath).into(this)
}

And it should be! Even the compiler will throw you an error saying there are 2 methods with the same JVM signature, because they are the same. Now this is a point of your own decision, so whichever option you choose to style your codebase is your own. I, myself, prefer the first option, because it’s not the ImageView that is doing the loading, Glide is.

As noted, this article is the second part of the series. Don’t forget to check the other ones too!

Filip Babić is an Android developer at COBE and a Computer Science student at FERIT, Osijek. He is a huge Kotlin fan, and occasionally holds mini work-shops and Kotlin meet-ups in Osijek. He likes to learn new stuff, play DnD and write about the things he loves the most. When he’s not coding, writing about coding, learning to code, or teaching others how to code, he nurtures his inner nerdiness by gaming and watching fantasy shows.

--

--

Writer for

Android developer. Praise Kotlin :] Keen and enthusiastic learner and mentor, passionate about teaching and helping others. GDE @ Android, Osijek, Croatia.