Preface: Kotlin is a very flexible language, which is a double-edged sword. Compared to Java, everyone writes in vernacular, and everyone can read each other’s code very smoothly, regardless of their level. However, after using Kotlin, due to the differences in Kotlin expression level and thinking habits, such a situation can be caused. “How the hell can you write that?” “And” What does it say? “, “Oh my God, cow B”.

Kotlin, as the language of choice for Android development, provides plenty of syntactic sugar and tricks for developers to focus on requirements with minimal language impact. The biggest difference between Java and Kotlin actually lies in Kotlin’s functional programming philosophy and syntax, especially lambda expressions, which are Kotlin’s core weapon for being more efficient than Java development. Let’s take a look at Lambda in detail.

Lambda expression is an important concept of Kotlin functional programming, to master functional programming, we must master lambda expression, and master its various writing and implementation, which are the foundation of functional programming.

Basic form of lambda

Lambda expressions have three characteristics:

Lambda expressions exist in {} 2. Arguments and argument types (omitted) are to the left of -> 3. Lambda expressions always return the value of the last line inside the function body. These three forms of lambda expressions must be mastered in order to further understand Kotlin and functional programming.

There is no parameter

Without parameters, the form is:

Function name = {function body}

Example:

Val hello = {println("hello kotlin")} // Equivalent to fun hello() {println("hello kotlin")}Copy the code

A parameter

(parameter 1 type, parameter 2 type,…) -> Return value type = {parameter 1, parameter 2… -> Function body}

Function name = {parameter 1: type 1, parameter 2: type 2,… -> Function body}

Example:

Val sum: (Int, Int) -> Int = {a, b -> a + b} // equivalent to val sum = {a: Int, b: > a + b} Int): Int { return a + b }Copy the code

When there is only one parameter, the parameter parameter in the return value can be omitted. When referencing lambda, there are two ways to invoke lambda through IT: one is invoked through (), and the other is invoked through the invoke() function. There is no difference between the two ways.

fun main(args: Array<String>) {
    val lambda = { println("test") }
    lambda()
    lambda.invoke()
}
Copy the code

When lambda expressions are used, an underscore (_) can be used to indicate unused arguments, indicating that the argument is not processed. Anonymous functions

The form of the anonymous function is:

Val = fun(parameter 1: type 1, parameter 2: type 2,…) : Return value type {function body}

Example:

Val sum = fun(a: Int, b: Int): Int {return a + b} // equivalent to fun sum(a: Int, b: Int): Int {return a + b}Copy the code

Evolution of higher order functions

The so-called higher-order function is actually the concept of compound function in mathematics, f(g(x)).

Reference function

fun cal(a: Int, b: Int, f: (c: Int, d: Int) -> Int): Int {
    return f(a, b)
}
fun sum(a: Int, b: Int): Int {
    return a + b
}
fun main(args: Array<String>) {
    val result = cal(2, 3, ::sum)
    println("result = $result")
    // result = 8
}
Copy the code

The last parameter in the CAL function is f: (a: Int, b: Int) -> Int indicates that the parameter is a function reference, and the function referred to by the last parameter is called in the function body. We then define the sum function, which is the third argument to the CAL function.

CAL (2, 3, ::sum) executes sum(2, 3) and outputs 5.

Function references can further simplify function calls, as in the following example:

class Test { fun doSomething() { println("test") } fun doTest(f: (Test) -> Unit) { f(this) } } fun main(args: Array<String>) {val t = Test(); // we pass the function t.totest {Test -> test.dosomething ()} Using the reference function (Test::doSomething is actually a simplification of the lambda expression {Test -> test.dosomething ()}) t.totest (Test::doSomething)}Copy the code

Parameter lambda

fun cal(a: Int, b: Int, f: (a: Int, b: Int) -> Int): Int {
    return f(a, b)
}

fun main(args: Array<String>) {
    val sum = { a: Int, b: Int -> a + b }
    val result = cal(2, 3, sum)
    println("result = $result")
    // result = 5
}
Copy the code

Rewrite a function as a lambda and assign it as an argument directly to the CAL function.

One step further, then, is to omit the lambda variable and pass the lambda expression directly into the function.

fun cal(a: Int, b: Int, f: (a: Int, b: Int) -> Int): Int { return f(a, b) } fun main(args: Array<String>) { val result = cal(2, 3, { a: Int, b: Int -> a + b}) println("result = $result") // result = 5} The parentheses can also be omitted if there are no other arguments. fun cal(a: Int, b: Int, f: (a: Int, b: Int) -> Int): Int { return f(a, b) } fun main(args: Array<String>) {result = CAL (2, 3, {a: Int, b: Int -> a + b}) Int -> a + b } println("result = $result") // result = 5 }Copy the code

The function variables

fun main(args: Array) { val sumLambda = {a: Int, b: Int -> a + b} var numFun: (a: Int, b: Int) -> Int numFun = {a: Int, b: Int -> a + b} lambda numFun = ::sum numFun(1,2)}Copy the code

You can see that this variable can be equal to a lambda expression, another lambda expression variable, or a normal function, but you need to prefix the function name with (::) to get the function reference.

Lambda expression instance

Here’s a simple example of how lambda expressions are written.

Val sum = fun(a: Int, b: Int): Int {return a + b} Int {return a + b} fun highSum(a: Int, b: Int, f: (Int, Int) -> Int): Int {return f(a, b)} fun main(args: Array<String>) {sum val add = sum(1, 2) println(add) = highSum val add2 = highSum(3, 3) {a, b -> a + b} println(add2) {a, b -> a + b} ::namedSum) println(add3) // The forEach argument takes a function args. ForEach ({it: String -> println(it)}) ForEach ({it -> println(it)}) // The lambda expression can be externalized with the last argument Args. ForEach () {println(it)} // Use args. ForEach (::println)}Copy the code

Function types and instantiations

For data types such as Int and String, the type of the function is:

(Type1, Type2, ...) Fun test(a: Int, f: (Int) -> Int): Int {return f(a)}Copy the code

The status of (Int) -> Int is equivalent to that of Int and String.

If a function is a type, it has instantiable instances like Int and String, for example, instance 1 of Int and instance “xys” of String.

The :: double colon operator represents a reference to a function. 2. Lambda expression 3. Anonymous functions

Println (test(1, 2, ::add)) val add = fun(a: Int, b: Int): Int {return a + b} println(test(3, 4, add)) // lambda expression println(test(5, 6, {a, Println (test(5, 6) {a, b -> a + b})} fun test(a: Int, b: Int, f: (Int, Int) -> Int): Int { return f(a, b) } fun add(a: Int, b: Int): Int { return a + b }Copy the code

The type of a lambda expression

To understand the type of a lambda expression, use the following example, as shown below.

// No arguments, return String ()-> String // Two integer arguments, return String type (Int, Int) -> String // A lambda expression and an integer are passed, return Int (()->Unit, Int) -> IntCopy the code

Developers can express the type of a lambda expression in a form similar to the one above, but like Int and String, lambda expressions have their own class, the Function class.

Kotlin encapsulates Function0 through Function22, with a total of 23 Function types representing the number of parameters from 0 to 22.

Lambda expression return###

Return returns from the most recent function declared using the fun keyword, unless the return point is specified using the tag.

fun main(args: Array<String>) {
    var sum: (Int) -> Unit = tag@{
        print("Test return $it")
        return@tag
    }
    sum(3)
}
Copy the code

SAM conversion

SAM = Single Abstract Method

SAM conversions are used to invoke one of the syntactic sugar provided by Java code in Kotlin code, namely an interface to Java’s single method, providing a lambda-like implementation, such as view.setonClickListener, the most common on Android:

SetOnClickListener {view -> doSomething} // Java interface public interface OnClickListener{void onClick(View v); }Copy the code

SAM transformations are syntactic sugar provided specifically for Java to convert lambda expressions into instances of the corresponding anonymous classes. The same functionality is achieved in Kotlin using only function parameters.

Lambda expression with receiver

Lambda expressions actually come in two forms, the basic form described earlier and the form with a receiver, as shown below.

Normal lambda expressions: {() -> R}

That is, the function has no input parameters and returns a value of type R.

Lambda expression with receiver: {T.() -> R}

That is, declare a receiver object of type T with no input parameters and return a value of type R.

The extension functions in Kotlin are essentially lambda expressions with receivers that are used,

The difference between a lambda with a receiver and a regular lambda is the orientation of this. In T.() -> R, this represents an instance of T itself, whereas in () -> R, this represents an instance of an external class.

Use typeAlias to alias repeated lambda expressions

fun fun1(f: (Int) -> Unit) { f(1) } fun fun2(f: (Int) -> Unit) {f(2)} typeAlias TypeAlias intFun = (Int) -> Unit Fun fun3(f: intFun) {f(3)} fun4(f: intFun) { f(4) } fun main(args: Array<String>) { fun1 { println(it) } fun2 { println(it) } fun3 { println(it) } fun4 { println(it) } }Copy the code

closure

If a function declares or returns a function internally, the function is called a closure.

Variables inside a function can be accessed and modified by functions declared inside the function, which allows closures to carry state (all intermediate values are put into memory).

Closures allow functions to have state, which encapsulates the state of the function and makes it object-oriented.

Why do YOU need closures

Before we get to closures, we need to understand the scope of variables. In Kotlin, there are only two types of scope for variables: global variables and local variables.

Global variables, function inside and function outside can be accessed directly.

Local variables, accessible only from the inside of the function.

How do you access local variables inside a function from outside of it? This is done through closures, which are designed to allow developers to read variables inside a function.

So closures are functions that can read local variables of other functions.

Closures let functions carry state

fun test(): () -> Int {
    var a = 1
    println(a)
    return fun(): Int {
        a++
        println(a)
        return a
    }
}

fun main(args: Array<String>) {
    val t = test()
    t()
    t()
}

// output
1
2
3
Copy the code

The type of the variable t is actually an anonymous function, so when the function t is called to execute, it is actually executing the returned anonymous function. Also, since the closure can carry outsourced variable values, the state value of A is passed down.

Closures can access variables outside the function body, which is called variable capture. Closures carry state by storing captured variables in a special area of memory.

Kotlin implements interface callbacks

Single method callback

class Test {
    private var callBack: ((str: String) -> Unit)? = null
    fun setCallback(myCallBack: ((str: String) -> Unit)) {
        this.callBack = myCallBack
    }
}
Copy the code

Use functions instead of interface implementations.

The evolution of callback writing

Kotlin’s writing of Java ideas

interface ICallback { fun onSuccess(msg: String) fun onFail(msg: String) } class TestCallback { var myCallback: ICallback? = null fun setCallback(callback: ICallback) { myCallback = callback } fun init() { myCallback? .onSuccess("success message") } } fun main(args: Array<String>) { val testCallback = TestCallback() testCallback.setCallback(object : ICallback { override fun onSuccess(msg: String) { println("success $msg") } override fun onFail(msg: String) {println("fail $MSG ")}}) testcallback.init ()} uses lambda expressions instead of anonymous inner class implementations. class TestCallback { var mySuccessCallback: (String) -> Unit? = {} var myFailCallback: (String) -> Unit? = {} fun setCallback(successCallback: (String) -> Unit, failCallback: (String) -> Unit) { mySuccessCallback = successCallback myFailCallback = failCallback } fun init() { mySuccessCallback("success message") myFailCallback("fail message") } } fun main(args: Array<String>) { val testCallback = TestCallback() testCallback.setCallback({ println("success $it") }, { println("fail $it") }) testCallback.init() }Copy the code

This removes interfaces and anonymous inner classes.

Use scenarios for higher-order functions

An important use case for higher-order functions is collection operations. In the following example, Java and Kotlin implement the “find the maximum” method, respectively.

data class Test(val name: String, val age: Int) fun main(args: Val testList = listOf(Test("xys", 18), Test("qwe", 12), Test("rty", 10), Test(" ZXC ", Println (testList.maxby {it. Age}) println(testlist.maxby (Test::age))} fun findMax(test: List<Test>) { var max = 0 var currentMax: Test? = null for (t in test) { if (t.age > max) { max = t.age currentMax = t } } println(currentMax) }Copy the code

Functional set operations

fliter & map

Filter is used to filter data, similar to filterIndexed, which means a filter with Index, and filterNot, which means to filter all data that does not meet the criteria.

Map is used to transform data and represents a one-to-one transformation relation, which can transform data in the set once, similarly to mapIndexed().

fun main(args: Array<String>) { val test = listOf(1, 3, 5, 7, Println (" ${test.filter {it > 5}}") // The filter function iterates through the collection and selects those elements that will return true when applied to the given lambda Println (" square operation ${test.map {it * it}}") val testList = listOf(test ("xys", 18), Test("qwe", 12), Test("rty", 10), Test("zxc", Println (" only name ${testlist.map {it. Name}}") // filter and map chain println(" show name ${testlist.map {it. Name}}" ${testList.filter { it.age > 10 }.map { it.name }}") } data class Test(val name: String, val age: Int)Copy the code

all & any & count & find

fun main(args: Array<String>) { val test = listOf(1, 3, 5, 7, Println (" all >10 ${test.all {it >10}}") // any check whether there are any data that satisfy the conditions of the lambda expression Println (" exists >8 ${test.any {it >8}}") // count (" exists >8 ${test.any {it >8}}") // Count (" exists > 5 ${test.count {it > 5}}") // Println (" first > 5 ${test.find {it > 5}}") println(" last > 5 ${test.findLast {it > 5}}")}Copy the code

groupBy & partition & flatMap

FlatMap () represents a one-to-many relationship that transforms each element into a new set and tils it into a set.

The groupBy() method returns a Map<k,list>, where Key is the group condition and value is the set. </k,list

fun main(args: Array<String>) { val test = listOf("a", "ab", "b", Println (" ${test.groupby (String::first)}") // Partition is grouped according to the conditions of the lambda expression. This condition only supports Boolean type conditions. ForEach {print("$it, ")} println() test.partition {it. Length > 1 }.second. ForEach {print("$it, ")} println() Println (test.flatmap {it.tolist ()})}Copy the code

sortedBy

SortedBy () is used to make a descending order according to the specified rule.

fun main(args: Array<String>) {
    val test = listOf(3, 2, 4, 6, 7, 1)
    println(test.sortedBy { it })
}
Copy the code

take & slice

Take () and slice() are used to slice data, returning a new set of specified conditions from a set. Similar examples include takeLast(), takeIf(), and so on.

fun main(args: Array<String>) { val test = listOf(3, 2, 4, 6, 7, Println (test.take(3)) println(test.slice(2, 4))}Copy the code

reduce

Fun main(args: Array<String>) {val test = listOf("a", "ab", "b", "BC ") // reduce takes all the elements of a set and passes them through the operation function to achieve the cumulative effect of the data set. println(test.reduce { acc, name -> "$acc$name" }) }Copy the code

Scope function

The scope function mentioned in the previous article is an important example of a higher-order function.

Other features of lambda expressions

Lazy sequence operation

When chain calls are made to some collection functions, the result of each function call is saved as a new temporary list. Therefore, a large number of chain operations create a large number of intermediate variables, resulting in performance problems. To improve efficiency, chain operations can be changed to sequance.

Call the extension function asSequence to convert any set to a sequence, and call toList to do the reverse conversion

fun main(args: Array<String>) { val testList = listOf(Test("xys", 18), Test("qwe", 12), Test("rty", 10), Test("zxc", {testlist.filter {it. Age > 10}. Map {it. Name}}" Println (" name ${testlist.assequence ().filter {it.age > 10}.map {it.name}.tolist ()}")} data class  Test(val name: String, val age: Int)Copy the code

A complete sequence consists of two operations, the middle sequence, which is always lazy, and the end sequence, which triggers all lazy calculations.

Testlist.assequence ().filter {.. }.map {.. }.toList()Copy the code

Therefore, by using sequences in this way, you avoid creating a large number of intermediate sets, thus improving performance.

### Delay calculation

fun main(args: Array<String>) {
    val addResult = lateAdd(2, 4)
    print(addResult())
}

fun lateAdd(a: Int, b: Int): Function0<Int> {
    fun add(): Int {
        return a + b
    }
    return ::add
}
Copy the code

In lateAdd, a local function is defined and a reference to the local function is returned. The () operator is used to get the final result and delay the calculation.

fun main(args: Array<String>) { val funs = mapOf("sum" to ::sum) val mapFun = funs["sum"] if (mapFun ! = null) { val result = mapFun(1, 2) println("sum result -> $result") } } fun sum(a: Int, b: Int): Int { return a + b }Copy the code

Master lambda expression, equal to the soldier with a gun, more practice, more thinking, to master the essence of lambda expression, but also to master functional programming, lay a solid foundation. How Kotlin improves the efficiency of development, and its class delegate is not explained here, the details can be found in the following document ####Kotlin basic introduction to advanced intensive field information sharing: Today’s share is divided into two parts: bytedance factory internal ultra-high quality Kotlin notes, Google executives wrote advanced Kotlin intensive practice (with Demo).

1. Ultra-high quality Kotlin notes in bytedance factory

First the table of contents is presented:

1. Get ready to start

Main contents: basic grammar, idioms, coding style

Basis of 2.

Main contents: basic types, packages, control flow, returns and jumps

3. Classes and objects

Main Content: Classes and inheritance, properties and fields, interfaces, visibility modifiers, extensions, data objects, generics, nested classes, enumerated classes, object expressions and declarations, proxy patterns, proxy properties

4. Functions and lambda expressions

Main contents: functions, higher-order functions and lambda expressions

5. Other

Main Contents: Multiple declarations, Ranges, type checking and automatic conversions, This expressions, equations, operator overloading, null safety, exceptions, annotations, reflection, dynamic typing

6. Interoperability

Main Content: Dynamic type

7. Tools

Main contents: Use Maven, use Ant, use Gradle, use Griffon

8.FAQ

Main content: Comparison with Java and Scala

Click here for free:Bytedance internal ultra high quality Kotlin notes

After basic knowledge is mastered rely on actual combat to promote!

Two, Google senior Kotlin preparation to strengthen the actual combat (with Demo)

Finally, why did we choose Kotlin?

Brevity: Greatly reduces the amount of boilerplate code. Safety: Avoid whole class errors such as null pointer exceptions. Interoperability: Leverage existing libraries of the JVM, Android, and browser. Tool-friendly: Build with any Java IDE or use the command line.

All of the materials in this article are available free of charge,If you just need to click here to get free.

Quick Access: Download it here! Sincerity full!!

Feel helpful friends can help like share support small series ~

Your support, my motivation; I wish you a bright future and continuous offer!!