6 min read

This article by Igor Wojda and Marcin Moskala, authors of the book Android Development with Kotlin, introduces functions in Kotlin, together with different ways of calling functions.

(For more resources related to this topic, see here.)

Single-expression functions

During typical programming, many functions contain only one expression. Here is example of this kind of function:

fun square(x: Int): Int { 
    return x * x 
} 

Or another one, which can be often found in Android projects. It is pattern used in Activity, to define methods that are just getting text from some view or providing some other data from view to allow presenter to get them:

fun getEmail(): String { 
   return emailView.text.toString() 
} 

Both functions are defined to return result of single expression. In first example it is result of x * x multiplication, and in second one it is result of expression emailView.text.toString(). This kind of functions are used all around Android projects. Here are some common use-cases:

  • Extracting some small operations
  • Using polymorphism to provide values specific to class
  • Functions that are only creating some object
  • Functions that are passing data between architecture layers (like in preceding example Activity is passing data from view to presenter)
  • Functional programming style functions that base on recurrence

Such functions are often used, so Kotlin has notation for this kind of them. When a function returns a single expression, then curly braces and body of the function can be omitted. We specify expression directly using equality character. Functions defined this way are called single-expression functions. Let’s update our square function, and define it as a single-expression function:

As we can see, single expression function have expression body instead of block body. This notation is shorter, but whole body needs to be just a single expression. In single-expression functions, declaring return type is optional, because it can be inferred by the compiler from type of expression. This is why we can simplify use square function, and define it this way:

fun square(x: Int) = x * x 

There are many places inside Android application where we can utilize single expression functions. Let’s consider RecyclerView adapter that is providing layout ID and creating ViewHolder:

class AddressAdapter : ItemAdapter<AddressAdapter.ViewHolder>() { 
 
   override fun getLayoutId() = R.layout.choose_address_view 
   override fun onCreateViewHolder(itemView: View) = ViewHolder(itemView) 
 
   // Rest of methods 
} 

In the following example, we achieve high readability thanks to single expression function. Single-expression functions are also very popular in the functional world. Single expression function notation is also well-pairing with when structure. Here example of their connection, used to get specific data from object according to key (use-case from big Kotlin project):

fun valueFromBooking(key: String, booking: Booking?) = when(key) { // 1 
   "patient.nin" -> booking?.patient?.nin 
   "patient.email" -> booking?.patient?.email 
   "patient.phone" -> booking?.patient?.phone 
   "comment" -> booking?.comment 
   else -> null 
}

We don’t need a type, because it is inferred from when expression. Another common Android example is that we can combine when expression with activity method onOptionsItemSelected that handles top bar menu clicks:

override fun onOptionsItemSelected(item: MenuItem): Boolean = when { 
    item.itemId == android.R.id.home -> { 
        onBackPressed() 
        true 
    } 
    else -> super.onOptionsItemSelected(item) 
} 

As we can see, single expression functions can make our code more concise and improved readability. Single-expression functions are commonly used in Kotlin Android projects and they are really popular for functional programming.

As an example. Let’s suppose that we need to filter all the odd values from following list:

val list = listOf(1, 2, 3, 4, 5) 

We will use following helper function that returns true if argument is odd otherwise it returns false:

fun isOdd(i: Int) = i % 2 == 1 

In imperative programming style, we should specify steps of processing, which are connected to execution process (iterate through list, check if value is odd, add value to one list if it’s odd). Here is implementation of this functionality, that is typical for imperative style:

var oddList = emptyList<Int>() 
for(i in list) { 
   if(isOdd(i)) { 
       newList += i 
   } 
} 

In declarative programming style, the way of thinking about code is different – we should think what is the required result and simply use functions that will give us this result. Kotlin stdlib provides lot of functions supporting declarative programming style. Here is how we could implement the same functionality using one of them, called filter:

var oddList = list.filter(::isOdd) 

filter is function that leaves only elements that are true according to predicate. Here function isOdd is used as an predicate.

Different ways of calling a function

Sometimes we need to call function and provide only selected arguments. In Java we could create multiple overloads of the same method, but this solution have there are some limitations. First problem is that number of possible method permutations is growing very quickly (2n) making them very difficult to maintain. Second problem is that overloads must be distinguishable from each other, so compiler will know which overload to call, so when method defines few parameters with the same type we can’t define all possible overloads. That’s why in Java we often need to pass multiple null values to a method:

// Java 
printValue("abc", null, null, "!"); 

Multiple null parameters provide boilerplate and greatly decrease method readability. In Kotlin there is no such problem, because Kotlin has feature called default arguments and named argument syntax.

Default arguments values

Default arguments are mostly known from C++, which is one of the oldest languages supporting it. Default argument provides a value for a parameter in case it is not provided during method call. Each function parameter can have default value. It might be any value that is matching specified type including null. This way we can simply define functions that can be called in multiple ways

We can use this function the same way as normal function (function without default argument values) by providing values for each parameter (all arguments):

printValue("str", true, "","")  // Prints: (str)

Thanks to default argument values, we can call a function by providing arguments only for parameters without default values:

printValue("str")  // Prints: (str) 

We can also provide all parameters without default values, and only some that have a default value:

printValue("str", false)  // Prints: str 

Named arguments syntax

Sometimes we want only to pass value for last argument. Let’s suppose that we define want to define value for suffix, but not for prefix and inBracket (which are defined before suffix). Normally we would have to provide values for all previous parameters including the default parameter values:

printValue("str", true, true, "!") // Prints: (str) 

By using named argument syntax, we can pass specific argument using argument name:

printValue("str", suffix = "!") // Prints: (str)! 

We can also use named argument syntax together with classic call. The only restriction is when we start using named syntax we cannot use classic one for next arguments we are serving:


printValue("str", true, "") 
printValue("str", true, prefix = "") 
printValue("str", inBracket = true, prefix = "")

Summary

In this article, we learned about single expression functions as a type of defining functions in application development. We also briefly explained

Resources for Article:

 


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here