They are all tightly interconnected and in fact you can’t use HOF without using lambdas or anonymous functions. For those not familiar with the basics: HOF allow to accept other functions as parameters or return the functions as work result.
JS-developers use these things every other day, while doing the AJAX-request and transmitting anonymous functions as callbacks. In the very Python this has been implemented initially and is known by the term “decorators”. It is simple and convenient and not accessible to every Android developer.
If you have a function ready-and-waiting and you want to send it as a parameter you can do it by placing ‘::’ before its name. Moreover, in Kotlin 1.1 they added the ability to transfer the methods of a class instance in the same way. Let’s look into a simple example to see how it works. The task is the following: we have a list of lines and we need to select only those with even length. Our extension function— isEven() will help here.
fun Int?.isEven() : Boolean = this ?.rem(2) == 0 //just a dummy func used for an experiment fun check(str: String): Boolean = str.length.isEven() //@param validator - it’s a function that takes String param and returns Boolean value fun sortOutStrings(list: List, validator: (String) -> Boolean): List { val result = arrayListOf() for (item in list) if (validator(item)) result.add(item) return result }//sending reference of our check() function inside sortOutStrings()
println(sortOutStrings(listOf("A", "AB", "ABC"), ::check)) //prints [AB]Similarly, the filter() function in Collections receives lambda/function( the result of which is Boolean) to filter out the data and return the result collection.
In fact, this example still looks cumbersome, let’s try and simplify it using an anonymous function and a lambda:
//anonymous function
println(sortOutStrings(listOf("A", "AB", "ABC"), fun(str) = str.length.isEven())) //prints [AB]//lambda with the chain of other functions that process the result of each other
println(listOf("abc", "ab", "a").filter{!it.length.isEven()}.sortBy{it}.map{it.toUpperCase})//prints [A, ABC]//lambdas
println(sortOutStrings(listOf("A", "AB", "ABC"), {it.length.isEven()})) //prints [AB] println(sortOutStrings(listOf("A", "AB", "ABC")){it.length.isEven()}) //prints [AB]The last two lines of code are especially interesting – in fact it is the same structure, but the syntax is slightly different. It is the implicit name of a single parameter. If the last parameter in the call is a function, then its body can be defined outside of parentheses, immediately after them – in the curly brackets. And that’s why it’s great: in this way you can define blocks consisting of several functions, and this is implemented using the function literal with a specified receiver object. On the one hand, this is very similar to the extension functions, because we can call the methods of this object without any additional qualifiers, only now we can pass the set of methods of this object that we want to call. Consider an example with TextView:
inline fun textview(parent: ViewGroup, setup: TextView.() -> Unit): TextView { val view = TextView(parent.context) view.setup() parent.addView(view) return view }The textview() function takes a setup lambda as the last parameter with the explicitly specified type of receiver object – TextView, and you can use it like this:
textview(container) { layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) setText(R.string.app_name) // OR text = getString(R.string.app_name) setTextColor(Color.RED) textSize = 12 f; setOnClickListener { Toast.makeText(context, "That's Me!", Toast.LENGTH_LONG).show() } }It means that all the code that is inside the parentheses will be executed in the place where the call to view.setup() occurs. This approach is very convenient for implementing builders and tree structures. If you liked this approach, I advise you to look for a separate section in the documentation on Type-Safe Builders, there is an excellent example of building HTML-markup using the approach of function with receiver object.
In the description of the textview() function there is an inline keyword. It tells the compiler that the code for this function (ie its body) needs to be inserted directly into the place where this function is triggered in its pure form. Thus, we save memory (cause we omit creation of one more abstraction), we do not lose in performance, BUT the number of output code grows slightly. It should not be forgotten and, if possible, you should use inline as much as possible and take all benefit out of it. For example, extension functions for base types are implemented in this very approach.
Going back to lambdams, it is worth noting that in Kotlin 1.1 they have added destructuring support and the ability to skip unnecessary parameters with the underscore “_”.
//this is our distracted data class data class DistractedDataClass(val id: Int, val text: String, val weight: Int) //some threshold value we want filter data against val threshold = 6; val dummyList = listOf(DistractedDataClass(0, "Hello", 1), DistractedDataClass(1, "World", 2)) //we don’t need first parameter for inner logic of lambda, so missing it in lambda declaration val goodList = dummyList.filter { (_, txt, w) -> txt.length + w > threshold } if (goodList.size > 0) println("${goodList.get(0).toString()}") //prints “DistractedDataClass(id=1, text=World, weight=2)”