Table of contents
Standard functions
Kotlin
The standard library contains several functions whose sole purpose is to execute blocks of code within the context of objects.lambda
When 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 objectit.
. The return value islambda
the 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:
run
A context object can be used as a receiver (
this
), a reference object is usedthis.
or an object value is used directly. The return value islambda
the result of the last line of code in the expression.run
Performswith
the same operation as , but is called as an extension function of the context objectlet
. Very useful whenlambda
including 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:
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 islambda
the result of the last line of code in the expression. Recommended to use to call a function on the context object without providinglambda
a result.
var user = User()
// 以给定的接收器作为其接收器调用指定的函数块,并返回其结果。
val with = with(user) {
name = "宾有为"
func = "with"
1 // 返回值 1
}
print("run:${
user}\nreturn lambda result:${
with}")
Results of the:
apply
A context object can be used as a receiver (
this
), a reference object is usedthis.
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 objectapply
.apply
A 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:
also
The context object can be used as a parameter (
it
), which is used when referencing the objectit.
. 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:
takeIf
takeIf
It is a filter function similar toif
the keyword single object, and is used in thetakeUnless
opposite way. When called with an object, if the objectlambda
matches the condition,takeIf
the object will be returned. Otherwise, returnnull
.
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:
takeUnless
takeIf
It is a filter function similarelse
to the keyword, and is used in thetakeIf
opposite way. When called with an object, if the objectlambda
matches the condition,takeIf
the object will be returned. Otherwise, returnnull
.
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:
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:
summary
Kotlin
The standard library contains several functions whose sole purpose is to execute blocks of code within the context of objects.lambda
When 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
,apply
andalso
.
The difference between scope functions
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 return
the 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)
kotlin
There 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.fun
When using it, you only need to add keywords in front of the functiontailrec
. 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 test
front of the function. In the extension function, this
the object used to reference the receiver l type is not the current Test class.
As shown in the figure below, the function String
cannot be called through the type test
. We add String
the type in front of the function name, and the function can be referenced through the type again test
.
Extension functions can only be called by the receiver type, not by the class in which the extension function resides.
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
this
is 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
private
orprotected
members. - 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")
}
lambda
In the expression that implements a higher-order function (such as the call main
to the function pair in the figure testFun1
), if a return value is set, return
the code result of the last line will be defaulted. If it is not set, it will be returned Unit
.
operation result:
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, lambda
the expression of a higher-order function can also be split into lambda
expression 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
lambda
The expression is converted into an anonymous inner class implementation at the bottom. Every timelambda
the expression is called, a new anonymous class instance will be created, which will cause additional memory and performance overhead, but this overhead can beinline
lambda
eliminated through expressions. Functions modified withinline
keywords are also called inline functions.
Using inline
keywords 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.
inlineFun
No inline
function code decompilation results added:
inlineFun
Add inline
function code decompilation results:
inline
Through decompilation and comparison before and after adding the keyword twice , it can be seen inline
that the function transfers the expression to the caller, which reduces lambda
the overhead caused by creating a new anonymous class.
When you try to lambda
use it on a function without an expression inline
, the compiler will prompt a warning inline
that 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 inline
functions 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
inline
a functionlambda
to be inlined, use modifiersnoinline
to mark some of the unnecessaryinline
function arguments (lambda
expressions only). The prerequisite for usenoinline
is that the function used must beinline
a function.
inline fun inlineFun(noinline action: (() -> Unit)){
println("inlineFun: 调用前...")
action()
println("inlineFun: 调用后...")
}
fun main(args: Array<String>) {
inlineFun{
println("inlineFun: 调用中...")
}
}
After adding noinline
the code for decompilation, the characters "Calling..." were not decompiled, but used to action$iv.invoke();
call the function.
crossinline
crosinline
lambda
Used to disable non-local returns in arguments passed to inline functions .
lambda
In the expression of an inline function , using it return
will interrupt the execution of the code behind the higher-order function. When talking about inline
functions, we have explained that inline
the 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
}
}
Using crossinline
keywords, you can prevent this situation. inline
After the function is added crossinline
, an error return
will be reported 'return' is not allowed here
.
Some blogs crossinline
interpret 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 return
does not affect the execution of the code behind the function that uses the higher-order function, it can be used return
, as follows:
anonymous function
A function whose name is omitted is called an anonymous function.
return
Anonymous 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 lambda
are similar to expressions, with different writing methods, but the execution effect can be the same. The following lambda
function 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 lambda
expressions are anonymous functions. The kotlin
anonymous 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 lambda
expressed by other authors to be anonymous functions. lambda
Where.
In the introduction to anonymous functions in the official documentation, the official explains lambda
the difference between anonymous functions and expressions:lambda表达式和匿名函数之间的另一个区别是非本地返回的行为。没有标签的return语句总是从用fun关键字声明的函数返回。这意味着lambda表达式内的返回将从封闭函数返回,而匿名函数内的返回则从匿名函数本身返回。
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
lambda
expressions.
top-level function
Kotlin allows functions (class, method...) to be defined directly within the file. This method can be called a top-level function.
The top-level function call is the same as the ordinary function call, just call it directly.
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...