[Kotlin] There are so many Kotlin functions, how many do you know?

Standard functions

KotlinThe standard library contains several functions whose sole purpose is to execute blocks of code within the context of objects. lambdaWhen such a function is called on an object that provides an expression, it forms a temporary scope. Within this scope you can access objects without names. Such functions are called scope functions. Technically speaking, scope functions are used interchangeably in many situations.

let

The context object can be used as a parameter ( it), which is used when referencing the object it.. The return value is lambdathe result of the last line of code in the expression. Can be used to call one or more functions on the result of a call chain.

var user = User()
// 使用此值作为参数调用指定的函数块并返回其结果。
val let = user.let {
    
    
    it.name = "宾有为"
    it.func = "let"
    1 // 返回值 1
}
print("let:${
      
      user},return lambda result:${
      
      let}")

Results of the:

Insert image description here

run

A context object can be used as a receiver ( this), a reference object is used this.or an object value is used directly. The return value is lambdathe result of the last line of code in the expression. runPerforms withthe same operation as , but is called as an extension function of the context object let. Very useful when lambdaincluding both object initialization and return values .run

var user = User()
// 调用指定的函数块,将此值作为其接收器并返回其结果。
val run = user.run {
    
    
    name = "宾有为"
    func = "run"
    1 // 返回值 1
}
print("run:${
      
      user}\nreturn lambda result:${
      
      run}")

Results of the:

Insert image description here

with

Non-extension functions: the context object is passed as a parameter, but internally lambda, it is available as a receiver ( this). The return value is lambdathe result of the last line of code in the expression. Recommended to use to call a function on the context object without providing lambdaa result.

var user = User()
// 以给定的接收器作为其接收器调用指定的函数块,并返回其结果。
val with = with(user) {
    
    
    name = "宾有为"
    func = "with"
    1 // 返回值 1
}
print("run:${
      
      user}\nreturn lambda result:${
      
      with}")

Results of the:

Insert image description here

apply

A context object can be used as a receiver ( this), a reference object is used this.or an object value is used directly. The return value is the object itself. Use for blocks of code that do not return a value and operate primarily on members of the receiver object apply. applyA common case is object configuration.

var user = User()
// 使用此值作为其接收器调用指定的函数块,并返回此值。
val apply = user.apply {
    
    
    name = "宾有为"
    func = "apply"
}
print("also:${
      
      apply}\nreturn context object:${
      
      apply}")

Results of the:

Insert image description here

also

The context object can be used as a parameter ( it), which is used when referencing the object it.. The return value is the object itself. Also suitable for performing some operations that take a context object as a parameter. It can also be used for operations that require a reference to an object rather than its properties and functions, or when you do not want to hide this reference from the external scope.

var user = User()
// 使用此值作为参数调用指定的函数块并返回此值。
val also = user.also {
    
    
    it.name = "宾有为"
    it.func = "also"
}
print("also:${
      
      user}\nreturn context object:${
      
      also}")

Results of the:

Insert image description here

takeIf

takeIfIt is a filter function similar to ifthe keyword single object, and is used in the takeUnlessopposite way. When called with an object, if the object lambdamatches the condition, takeIfthe object will be returned. Otherwise, return null.

var user = User(name = "宾有为", func = null)
val existName = user.takeIf {
    
     it.name != null }
val existSex = user.takeIf {
    
     it.func != null }
println("existName: $existName, existSex: $existSex")

Results of the:

Insert image description here

takeUnless

takeIfIt is a filter function similar elseto the keyword, and is used in the takeIfopposite way. When called with an object, if the object lambdamatches the condition, takeIfthe object will be returned. Otherwise, return null.

var user = User(name = "宾有为", func = null)
val existName = user.takeUnless {
    
     it.name != null }
val existSex = user.takeUnless {
    
     it.func != null }
println("existName: $existName, existSex: $existSex")

Results of the:

Insert image description here

repeat

repeat, is a function that loops from 0 to a specified length, for (index in 0 until times) { }consistent with the execution result.

// 从0遍历至10
repeat(10){
    
    
    print(it)
}

Results of the:

Insert image description here

summary

KotlinThe standard library contains several functions whose sole purpose is to execute blocks of code within the context of objects. lambdaWhen such a function is called on an object that provides an expression, it forms a temporary scope. Within this scope you can access objects without names. Such functions are called scope functions. There are five scope functions: let, run, with, applyand also.

The difference between scope functions

Insert image description here

Scope function usage scenarios

function scenes to be used
let 1. Execute lambda on non-null objects
2. Introduce expressions as variables in the local scope
with Group function calls on an object
run 1. Object configuration and calculation results
2. Run statements where expressions are required
apply 1. Object configuration and calculation results
2. Code blocks that do not return values ​​and mainly operate on members of the receiver object
also 1. Perform some operations that take the context object as a parameter.
2. Operations that require a reference to an object rather than its properties and functions
3. When you do not want to hide this reference from the external scope

Simplify function

In kotlin, variables can be assigned using the equal sign, and functions are also allowed to be assigned using the equal sign. These functions that use the equal sign to assign values ​​are called simplified functions.

There are requirements for writing simplified functions. If the function expression has only one line, you can use the equal sign to assign its expression to the function. Simplify returnthe last line of code in the function's default function.

fun main(args: Array<String>) {
    
    
    println(test1())// result:2
    println(test2())// result:简化函数
}
// 执行表达式 1+1
private fun test1() = 1+1
// 返回表达式"简化函数",简化函数如果有返回类型,表达式的执行结果必须是一个可以返回的类型。
private fun test2() : String = "简化函数"

Tail recursive function (tailrec)

kotlinThere is a special kind of recursive function - tail recursive function, which means that the return value at the end of the function repeatedly calls its own function. funWhen using it, you only need to add keywords in front of the function tailrec. The compiler will automatically optimize recursion during compilation and use loops instead of recursion to avoid stack overflow and improve program performance.

fun main(args: Array<String>) {
    
    
    print(factorial(5)) // result:120
}

// 求 i 的阶乘( i * i-1 )
tailrec fun factorial(i: Int): Int {
    
    
    if (i != 1) {
    
    
        return i * factorial(i - 1)
    } else {
    
    
        return i
    }
}

extension function

Put the receiver type in front of the function to be added, and the function that can be called through the class is called an extension function.

As shown in the figure, the receiver type is written in testfront of the function. In the extension function, thisthe object used to reference the receiver l type is not the current Test class.

Insert image description here
As shown in the figure below, the function Stringcannot be called through the type test. We add Stringthe type in front of the function name, and the function can be referenced through the type again test.

Insert image description here

Insert image description here
Extension functions can only be called by the receiver type, not by the class in which the extension function resides.

Insert image description here

Extension functions can not only extend functions, but also properties.

val String.lastIndex: Int
    get() = 0

fun main(args: Array<String>) {
    
    
    var a = "aaaa000"
    print(a.lastIndex) // result:0
}

tips

  • Extension functions cannot be overridden.
  • Extension functions are essentially static functions.
  • The type of reference used in an extension function thisis the type of the extension function, not the class in which the function currently resides.
  • If an extension function is declared outside its receiver type, it cannot access the receiver privateor protectedmembers.
  • The code priority of extension functions and attributes is higher than the original functions and objects of the receiver type, which is equivalent to overriding the functions and attributes of the same name of the receiver.

higher order function

Higher-order functions are functions that take functions as arguments or return functions.

In the following example code, testFun1(i: Int, face: (String) -> String)it is a higher-order function because it accepts a function value as its second parameter.

fun main(args: Array<String>) {
    
    
    testFun1(1) {
    
     it ->
        // 实现业务逻辑,将it给return回去
        it
    }
}

// face: (String) -> String  等于 调用高阶函数使用的名称: (需要传递的参数类型) -> 返回类型
fun testFun1(i: Int, face: (String) -> String) {
    
    
	// 接收到face返回值,并将其print
    val value = face("testFun")
    println("testFun:$value")
}

lambdaIn the expression that implements a higher-order function (such as the call mainto the function pair in the figure testFun1), if a return value is set, returnthe code result of the last line will be defaulted. If it is not set, it will be returned Unit.

operation result:

Insert image description here

In addition to the above usage methods, high-order functions can also be passed in different functions to process logic according to needs.

fun main(args: Array<String>) {
    
    
	val plusResult = num1AndNum2(20, 30) {
    
     n1: Int, n2: Int ->
        // 执行运算    
        n1 + n2
    }
    println("$plusResult") // result:50

    val minusResult = num1AndNum2(20, 30) {
    
     n1: Int, n2: Int ->
        // 执行运算
        n1 - n2
    }
    println("$minusResult") // result:-10
}

fun num1AndNum2(num1: Int, num2: Int, block: (Int, Int) -> Int): Int {
    
    
    return block(num1, num2)
}

In addition, lambdathe expression of a higher-order function can also be split into lambdaexpression variables and anonymous functions, and passed into the higher-order function in the form of variables and anonymous functions respectively.

fun main(args: Array<String>) {
    
    
    val minusResult = num1AndNum2(20, 30, lambda1)
    println("$minusResult") // result:-10

    val aaa = num1AndNum2(20, 30, lambda2) // result:50
    println("$aaa")

	val anonymous1 = num1AndNum2(20, 30, a) // result:-10
    val anonymous2 = num1AndNum2(20, 30, b) // result:50
	// 匿名函数
	num1AndNum2(20,30,fun(x,y) = x + y) // result:50
}
// lambda表达式
val lambda1 = {
    
     x: Int, y: Int -> x - y }
// lambda表达式
val lambda2: (Int, Int) -> Int = {
    
     x: Int, y: Int -> x + y }
// 匿名函数
val a = fun(x : Int, y : Int): Int = x - y
// 匿名函数
val b = (fun(x : Int, y : Int): Int = x + y)

fun num1AndNum2(num1: Int, num2: Int, block: (Int, Int) -> Int): Int {
    
    
    return block(num1, num2)
}

Inline function (inline)

inline

lambdaThe expression is converted into an anonymous inner class implementation at the bottom. Every time lambdathe expression is called, a new anonymous class instance will be created, which will cause additional memory and performance overhead, but this overhead can be inline lambdaeliminated through expressions. Functions modified with inlinekeywords are also called inline functions.

Using inlinekeywords requires decompilation to see the effect.

fun inlineFun(action: (() -> Unit)){
    
    
    println("inlineFun: 调用前...")
    action()
    println("inlineFun: 调用后...")
}

fun main(args: Array<String>) {
    
    
    inlineFun {
    
    
        println("inlineFun: 正在调用...")
    }
}

Steps to decompile code using Idea and Android Studio development tools: Go to Tools > Kotlin > Show Kotlin Bytecodes > Decompile in the top menu bar of the development tool.

inlineFunNo inlinefunction code decompilation results added:

Insert image description here
inlineFunAdd inlinefunction code decompilation results:

Insert image description here
inlineThrough decompilation and comparison before and after adding the keyword twice , it can be seen inlinethat the function transfers the expression to the caller, which reduces lambdathe overhead caused by creating a new anonymous class.

When you try to lambdause it on a function without an expression inline, the compiler will prompt a warning inlinethat the impact on performance is expected to be negligible and should be used in conjunction with higher-order functions.

Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types.

It should be noted that inlinefunctions do not support modified variables (including variables holding higher-order functions) and can only be used to modify higher-order functions.

noinline

If you don't want everything passed to inlinea function lambdato be inlined, use modifiers noinlineto mark some of the unnecessary inlinefunction arguments ( lambdaexpressions only). The prerequisite for use noinlineis that the function used must be inlinea function.

inline fun inlineFun(noinline action: (() -> Unit)){
    
    
    println("inlineFun: 调用前...")
    action()
    println("inlineFun: 调用后...")
}

fun main(args: Array<String>) {
    
    
    inlineFun{
    
    
        println("inlineFun: 调用中...")
    }
}

After adding noinlinethe code for decompilation, the characters "Calling..." were not decompiled, but used to action$iv.invoke();call the function.

Insert image description here

crossinline

crosinlinelambdaUsed to disable non-local returns in arguments passed to inline functions .

lambdaIn the expression of an inline function , using it returnwill interrupt the execution of the code behind the higher-order function. When talking about inlinefunctions, we have explained that inlinethe function transfers the expression to the caller, so the "after call" of the following code will not print.

inline fun inlineFun(action: (() -> Unit)) {
    
    
    println("inlineFun: 调用前...")
    action()
    println("inlineFun: 调用后...")
}

fun main(args: Array<String>) {
    
    
    inlineFun {
    
    
        println("inlineFun: 调用中...")
        return
    }
}

Insert image description here

Using crossinlinekeywords, you can prevent this situation. inlineAfter the function is added crossinline, an error returnwill be reported 'return' is not allowed here.

Insert image description here

Some blogs crossinlineinterpret the meaning as "check whether there is one in the code return. If there is, the execution will fail." This statement is one-sided. The official documentation has explained that non-local returns are prohibited. If the higher-order function returndoes not affect the execution of the code behind the function that uses the higher-order function, it can be used return, as follows:

Insert image description here
Insert image description here

anonymous function

A function whose name is omitted is called an anonymous function.

returnAnonymous functions are often used in conjunction with higher-order functions. Anonymous functions implicitly contain the last line of code by default . There are three ways to write them:

val a = fun(str: String): String = "a"
val b = (fun(str: String): String = "b")

Parameters and return types are specified in the same way as for higher-order functions, and parameter types can be omitted if they can be inferred from the context. as follows:

// 匿名函数
println(fun(item) = "c")
// 高阶函数
fun println(str : (String) -> String){
    
    
    println(str("ttt"))
}

Anonymous functions lambdaare similar to expressions, with different writing methods, but the execution effect can be the same. The following lambdafunction is equivalent to the above anonymous function writing method:

var d: (it: String) -> String = {
    
     "d" }

var e: (String) -> String = {
    
     "e" }

var f = {
    
     it: String -> "f" }

I read several blogs, and most of them said that lambdaexpressions are anonymous functions. The kotlinanonymous function writing method I found in the official documents does not include expressions. At the same time, I don’t know the basis for the expressions lambdaexpressed by other authors to be anonymous functions. lambdaWhere.

In the introduction to anonymous functions in the official documentation, the official explains lambdathe difference between anonymous functions and expressions:lambda表达式和匿名函数之间的另一个区别是非本地返回的行为。没有标签的return语句总是从用fun关键字声明的函数返回。这意味着lambda表达式内的返回将从封闭函数返回,而匿名函数内的返回则从匿名函数本身返回。
Insert image description here

summary

  • When passing anonymous functions as arguments, put them within parentheses. The shorthand syntax that allows you to put functions outside parentheses only works with lambdaexpressions.

top-level function

Kotlin allows functions (class, method...) to be defined directly within the file. This method can be called a top-level function.

Insert image description here
The top-level function call is the same as the ordinary function call, just call it directly.

Insert image description here
The difference between top-level functions and extension functions:

  • There is no need to specify the target class name when defining.
  • Naturally, there is no need to specify an instance when calling.

Local function or nested function

In addition to allowing functions to be defined at the top level of a file, Kotlin also allows nested functions to be defined within existing functions, called local functions.

fun magic(): Int {
    
    
    val v1 = (0..100).random()

    fun foo(): Int {
    
    
        return v1 * v1
    }

    return foo()
}

Business scenario: A function will only be called multiple times in one place, and you do not want this function to be called in other places in the class. At this time, you need to use local functions to further restrict the access of this function.

Note: References to local functions can only be referenced after the code that declares the local function. Local functions cannot be referenced before the code that declares the local function.

Reference documents
1. High-order functions
2. Standard and scope functions
3. High-order functions in Kotlin
4. "Kotlin from scratch to proficient" - the use of functions
5. Android foundation building, detailed explanation of Kotlin extension functions - Guo Lin
6 , Kotlin Doc——Extensions
7. Kotlin Keywords and operators
8. A thorough understanding of the dazzling function family in Kotlin in one article...

Guess you like

Origin blog.csdn.net/baidu_41616022/article/details/128758195