• Kotlin Demystified: Understanding Sleep Lambda Syntax
  • Originally written by Nicole Borrelli
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: androidxiao

During my trip to Austria, I visited the Austrian National Library in Vienna. The Hall of Congress, in particular, is a stunning space that feels like something out of an Indiana Jones movie. The space around the room is that these doors are mounted on shelves, and it’s easy to imagine what secrets lie behind them.

It turns out, however, that they are simply libraries.

Let’s say we have an application that keeps track of books in the library. One day, we wanted to know what the longest and shortest books in the series were. After that, we write code that allows us to find these two:

val shortestBook = library.minBy { it.pageCount }val longestBook = library.maxBy { it.pageCount }
Copy the code

Perfect! But it left me wondering, how do these methods work? How do I know it? I just said it. PageCount. How do I do it?

The first thing I did was define minBy and maxBy, both of which were at collections.kt. Since they are almost identical, let’s look at maxBy, which starts at line 1559.

Where [Iterable] method is built on (https://developer.android.com/reference/java/lang/Iterable) interface, But if we do a small rewritten to use [Collection] (https://developer.android.com/reference/java/util/Collection) s, the renaming of some variables may be more long, easy to understand:

public inline fun <T, R : Comparable<R>> Collection<T>.maxBy(selector: (T) -> R): T? {
    if (isEmpty()) return null
    var maxElement = first()
    var maxValue = selector(maxElement)
    for (element in this) {
        val value = selector(element)
        if (maxValue < value) {
            maxElement = element
            maxValue = value
        }
    }
    return maxElement
}
Copy the code

We can see that it just takes each element in the Collection and checks to see if the value from the selector is greater than the maximum it sees. If so, the elements and values are saved. Finally, it returns the largest element it found. Pretty simple.

Whereas selector, which looks pretty neat, has to be something that allows us to use it.pagecount on it, so let’s look at it again.

There is even quite a bit of grammatical sugar in this line. In this case, for selector: (T) -> R is a function that takes a single argument T and returns some return value of type R.

One possible approach is for Kotlin to include a set of interfaces called FunctionN, where N is the number of arguments it accepts. Since we have a parameter, we can implement the Function1 interface and then use it in our code:

class BookSelector : Function1<Book, Int> {
   override fun invoke(book: Book): Int {
       return book.pageCount
   }
}
 
val longestBook = library.maxBy(BookSelector())
Copy the code

This certainly shows how it works. Selector is a Function1 that returns an Int when given Book. MaxBy then takes the Int and compares it to the value it has.

Incidentally, this also explains why the generic parameter R has the type R [implements] Comparable

. If R is not Comparable, we cannot do if (maxValue

And then the question is, how do we get from there to the cycle that we started? Let’s step through the process.

First, the code can be replaced with lambda, which has been reduced considerably:

val longestBook = library.maxBy({
    it.pageCount
})
Copy the code

The next step is that if the last argument to the method is lambda, we can close the parentheses and add lambda to the end of the line, as follows:

val longestBook = library.maxBy() {
    it.pageCount
}
Copy the code

Finally, if a method accepts only a lambda argument, we can abandon the () method entirely, which brings us back to the original code:

val longestBook = library.maxBy { it.pageCount }
Copy the code

But wait! How about Function1! Do I do allocation every time I use it?

That’s a great question! The good news is, no, you’re not. If you look again, you’ll see that maxBy is marked as an inline function. This happens at the source level at compile time, so while there are more samples of compiled code than initially appear, there is no significant performance impact and certainly no object allocation.

That’s great! Now, not only do we know the shortest (and longest) book in the library, we can better understand how maxBy works. We saw how Kotlin uses [FunctionN](#full) lambda’s interface and moves lambda expressions out of the function’s argument list. Finally, we know that when a function is called with only a lambda argument, the usual parentheses can be omitted entirely.

Check out the Google Developers blog for more exciting content, and stay tuned for more Kotlin articles!

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.