Kotlin high-order functions, you can learn after reading

foreword

Today we will talk about lambda expressions. Lambda expressions should be familiar to everyone. A very important feature was introduced in Java8, which liberates developers from the original cumbersome syntax, but it is limited to only the Java8 version. Kotlin makes up for this problem. The mixed programming of lambda expressions and Java in Kotlin can support versions below Java8.

Why Use Lambda Expressions in Kotlin

Regarding the problem of using lambda expressions in Kotlin, there are mainly the following advantages:

  • Kotlin's lambda expression has a more concise and easy-to-understand syntax to realize functions, freeing developers from the original redundant and long-winded syntax declaration. You can use functional programming operators such as filtering, mapping, and conversion to process collection data, so that your code is closer to the style of functional programming.
  • Versions below Java 8 do not support lambda expressions, while Kotlin is compatible with versions below Java 8 and has good interoperability. It is very suitable for the mixed development model of versions below Java 8 and Kotlin. It solves the bottleneck that versions below Java 8 cannot use lambda expressions.
  • The use of lambda expressions in the Java8 version is limited, it is not a closure in the true sense, and the lambda in Kotlin is the implementation that supports closures in the true sense. (explained below)

Kotlin functions are first-class functions, which means they can be stored in variables and data structures, passed as arguments to, and returned from other higher-order functions. For other non-function values, you can operate on functions in any possible way.

To achieve this, as a statically typed programming language, Kotlin uses a series of function types to represent functions and provides a set of specialized language constructs, such as lambda expressions.

1. Higher-order functions

A higher-order function is one that takes a function as an argument (or returns a function) . That is, a function can take another function as a parameter (or return function), and a function that uses other functions as parameters (or return function) is called a "higher-order function".

(1) The following is an example of a higher-order function:

fun stringMapper(str: String, mapper: (String) -> Int): Int {
    // Invoke function
    return mapper(str)
}

stringMapper()The parameters of the function are a Stringand a function that will Stringderive Intthe value the you pass it.

To call stringMapper(), you can pass a Stringand a function that satisfies the conditions of the second parameter (that is, a function that Stringtakes as input and Intoutputs ), as in the following example:

stringMapper("Android", { input ->
    input.length
})

If the anonymous function is the last parameter defined on a function, you can pass this parameter ()outside of , as follows:

stringMapper("Android") { input ->
    input.length
}

(2) Another good example is the functional , which takes an initial accumulator value and a composition function, and computes it by successively combining the current accumulator value with each collection element Build its return value, replacing this overlay:

//从[初始]值开始累加值,从左到右对当前累加器值和每个元素应用[操作]
fun <T, R> Collection<T>.fold(
    initial: R,
    combine: (acc: R, nextElement: T) -> R
): R {
    var accumulator = initial
    for (element: T in this) {
        accumulator = combine(accumulator, element)
    }
    return accumulator
}

In the code above, the parameter combinehas a function type (R, T) -> R, so it receives a function that takes two parameters of type Rand T, and returns a value Rof . It is called in a for loop and then assigns the return value to accumulator.

call fold, we need to pass it an instance of the function type as an argument, and lambda expressions (described in more detail below) are widely used in this case at higher-order function call sites:

val items = arrayOf(1, 2, 3, 4, 5)
//Lambdas是大括号括起来的代码块
items.fold(0, { acc: Int, i: Int -> //当lambda 有参数时,参数在前面,然后是 `->` 符号
    print("acc == $acc | i == $i | ")
    val result = acc + i
    println("result == $result")
    //如果不显式指定,lambda 中的最后一个表达式被认定为是返回值
    result
})

//参数类型在lambda中如果能自动推断则可以省略
val itemsStr = items.fold("Elements:", { acc, i -> acc + "" + i })

//函数引用也可以用于高阶函数调用
val products = items.fold(1, Int::times)

The print data is as follows:

acc == 0 | i == 1 | result == 1
acc == 1 | i == 2 | result == 3
acc == 3 | i == 3 | result == 6
acc == 6 | i == 4 | result == 10
acc == 10 | i == 5 | result == 15

1.1 Function type

Kotlin uses a series of function types, such as (Int) -> Stringto declare functions: val onClick: () -> Unit = .... These types have a special notation corresponding to the signature of functions, i.e. their parameters and return value:

  • (A, B) -> C: All function types have a parenthesized parameter type list and a return type. (A, B) -> CRepresents a function type that represents a function that takes two parameters of types A and B and returns a value of type C. The parameter type list can be empty, eg () -> A. UnitThe return type cannot be omitted ;
  • A.(B) -> C: A function type can have an additional receiver type, which is specified .before . Type A.(B) -> CIndicates that the parameter of type B can be called in the receiving object A, and the return value is a function of type C. Function literals with receivers are often used with these types;
  • Suspend function: A function of the suspend function attribute attribute type has a suspend modifier in the notation. For example: suspend () -> Unitor suspend A.(B) -> C.

The function type notation can optionally include the names of the function parameters: (x: Int, y: Int) -> Point. These names can be used to document the meaning of the parameters.

1. To specify that a function type can be empty, you can use parentheses:((Int, Int) -> Int)?

2. Function types can be combined using parentheses:(Int) -> ((Int) -> Unit)

3. The arrow symbol ->is right-associative, (Int) -> (Int) -> Unitand (Int) -> ((Int) -> Unit)represents the same type as , but ((Int) -> (Int)) -> Unitis different from the function type .

You can also use type aliases to give a function type an alternate name:

typealias ClickHandler = (Button, ClickEvent) -> Unit

1.2 Instantiation function type

There are several ways to obtain an instance of a function type:

(1) Use code blocks in function literals in the following form:

  • A lambda expression: { a, b -> a + b };
  • An anonymous function: fun(s: String): Int { return s.toIntOrNull() ?: 0 }.

A function literal with receiver can be used as a value of a function type with receiver.

(2) Use an instance of a custom class that implements the function type as an interface:

//定义IntTransformer类型,实现了 (Int) -> Int 接口
class IntTransformer : (Int) -> Int {
    override operator fun invoke(num: Int): Int = TODO()
}

val intFunction: (Int) -> Int = IntTransformer()

If the data is unambiguous enough, the compiler can infer the function type of the variable:

val result = { i: Int -> i * 2 }//推断出的类型是(Int) -> Int

For a method type that contains a receiver, if the type of the receiver and the remaining parameter types of the method type match the input parameters of the method type that does not explicitly define the receiver, the two can perform mutual assignment . For example, a method type String.(Int) -> Booleanis a method type that explicitly contains a receiver. The method type of the receiver is String, and the parameter type is Int, so it is equivalent to the method type. (String, Int) -> BooleanThis method type receives a Stringtype and a Inttype parameter, and the first Stringtype just matches the receiver , the remaining parameter types also match each other and can be considered equal.

//String,Int传入的参数类型后面的String表示返回值类型,times表示Int类型参数
val substringStr: String.(Int) -> String = { times ->
    this.substring(0, times)//times为5
}

val twoParameters: (String, Int) -> String = substringStr

//(A, B) -> C 所有的函数类型有一个带括号的参数类型列表和一个返回类型,
//表示一个类型,该类型表示具有类型 A 和 B 的两个参数并返回类型 C 的值的函数。
fun runTransformation(ss: (String, Int) -> String): String {
    return ss("Android", 5)
}

val trans = runTransformation(substringStr) //substringStr()函数作为参数传递给runTransformation()函数

The print data is as follows:

Andro

Note: The type of a function without a receiver is automatically inferred by default, even if the variable is initialized with a reference to the extension function. If you want to change it, specify the variable type explicitly.

1.3 Call function type instance

A value of a function type can be invoked by using its invoke(...)operator : f.invoke(x)or f(x). invoke()Represents 函数变量calling .

If the value has a receiver type, a receiver object should be passed as the first parameter. Another way to call a value of function type with a receiver is to prefix it with the receiver object, as if the value were an extension function: 1.foo(2).

//String.plus()通过将该字符串与给定的其他对象的字符串表示形式链接起来而获得的字符串
val stringPlus: (String, String) -> String = String::plus
val intPlus: Int.(Int) -> Int = Int::plus //Int.plus()将另一个值添加到此值

val str = stringPlus.invoke("<-", "->") //打印:<-->
val str2 = stringPlus("Hello", "World") //打印:HelloWorld

val int1 = intPlus(1, 1) //打印:2
val int2 = intPlus.invoke(2, 3) //打印:5
val int3 = 4.intPlus(5) //打印:9

This article is in the open source project: Android development does not know these? How to get a high salary in an interview has been included, which contains self-study programming routes in different directions, interview question collection/face-to-face, and a series of technical articles, etc. Scan the QR code below to receive it for free. The resources are continuously being updated...

Guess you like

Origin blog.csdn.net/Eqiqi/article/details/129875836