营销型网站建设标准,门户网站建设探究,永久免费的仓库,快站官网平台目录标准函数letrunwithapplyalsotakeIftakeUnlessrepeat小结作用域函数的区别作用域函数使用场景简化函数尾递归函数#xff08;tailrec#xff09;扩展函数高阶函数内联函数#xff08;inline#xff09;inlinenoinlinecrossinline匿名函数标准函数 Kotlin标准库包含几个…
目录标准函数letrunwithapplyalsotakeIftakeUnlessrepeat小结作用域函数的区别作用域函数使用场景简化函数尾递归函数tailrec扩展函数高阶函数内联函数inlineinlinenoinlinecrossinline匿名函数标准函数 Kotlin标准库包含几个函数其唯一目的是在对象上下文中执行代码块。当对提供了lambda表达式的对象调用这样的函数时它会形成一个临时作用域。在此范围内您可以访问不带名称的对象。此类函数称为作用域函数。从技术上讲作用域函数在许多情况下是可以互换使用的。 let 上下文对象可用作参数(it)引用对象时使用it.。返回值是lambda表达式最后一行代码的结果。可用于调用调用链结果上的一个或多个函数。 var user User()
// 使用此值作为参数调用指定的函数块并返回其结果。
val let user.let {it.name 宾有为it.func let1 // 返回值 1
}
print(let:${user}return lambda result:${let})执行结果 run 上下文对象可用作接收器(this)引用对象使用this.或直接使用对象值。返回值是lambda表达式最后一行代码的结果。run执行与with相同的操作但作为上下文对象的扩展函数调用let。当lambda同时包含对象初始化和返回数值时run非常有用。 var user User()
// 调用指定的函数块将此值作为其接收器并返回其结果。
val run user.run {name 宾有为func run1 // 返回值 1
}
print(run:${user}\nreturn lambda result:${run})执行结果 with 非扩展函数上下文对象作为参数传递但在lambda内部它作为接收器this可用。返回值是lambda表达式最后一行代码的结果。建议使用来调用上下文对象上的函数而不提供lambda结果。 var user User()
// 以给定的接收器作为其接收器调用指定的函数块并返回其结果。
val with with(user) {name 宾有为func with1 // 返回值 1
}
print(run:${user}\nreturn lambda result:${with})执行结果 apply 上下文对象可用作接收器(this)引用对象使用this.或直接使用对象值。返回值是对象本身。对于不返回值且主要对接收器对象的成员进行操作的代码块请使用apply。apply的常见情况是对象配置。 var user User()
// 使用此值作为其接收器调用指定的函数块并返回此值。
val apply user.apply {name 宾有为func apply
}
print(also:${apply}\nreturn context object:${apply})执行结果 also 上下文对象可用作参数(it)引用对象时使用it.。返回值是对象本身。也适用于执行一些将上下文对象作为参数的操作。还可用于需要引用对象而不是其属性和函数的操作或者不希望从外部范围隐藏此引用时。 var user User()
// 使用此值作为参数调用指定的函数块并返回此值。
val also user.also {it.name 宾有为it.func also
}
print(also:${user}\nreturn context object:${also})执行结果 takeIf takeIf是类似 if 关键字单个对象的过滤函数使用方式与takeUnless相反。使用对象进行调用时如果该对象与lambda的条件匹配takeIf将返回该对象。否则返回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)执行结果 takeUnless takeIf是类似 else 关键字的过滤函数使用方式与takeIf相反。使用对象进行调用时如果该对象与lambda的条件匹配takeIf将返回该对象。否则返回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)执行结果 repeat repeat是一个从0开始循环至指定长度的函数与for (index in 0 until times) { }执行的结果一致。 // 从0遍历至10
repeat(10){print(it)
}执行结果 小结 Kotlin标准库包含几个函数其唯一目的是在对象上下文中执行代码块。当对提供了lambda表达式的对象调用这样的函数时它会形成一个临时作用域。在此范围内您可以访问不带名称的对象。此类函数称为作用域函数。作用域函数有五个let、run、with、apply和also。 作用域函数的区别 作用域函数使用场景
函数使用场景let1、对非空对象执行lambda2、在局部范围中引入表达式作为变量with对对象的函数调用进行分组run1、对象配置和计算结果2、在需要表达式的地方运行语句apply1、对象配置和计算结果2、不返回值且主要对接收器对象的成员进行操作的代码块also1、执行一些将上下文对象作为参数的操作。2、需要引用对象而不是其属性和函数的操作3、不希望从外部范围隐藏此引用时
简化函数 在kotlin中变量可以通过等号赋值函数同样被允许使用等号进行赋值这些使用等号赋值的函数就叫做简化函数。 简化函数的编写规范是有所要求的如果函数表达式只有一行才可以使用等号赋予函数其表达式。简化函数默认return函数的最后一行代码。
fun main(args: ArrayString) {println(test1())// result2println(test2())// result简化函数
}
// 执行表达式 11
private fun test1() 11
// 返回表达式简化函数简化函数如果有返回类型表达式的执行结果必须是一个可以返回的类型。
private fun test2() : String 简化函数尾递归函数tailrec kotlin存在一种特殊的递归函数——尾递归函数指的是函数末尾的返回值重复调用了自身函数。使用时只需要在函数的fun前面加上tailrec关键字编译器在编译时会自动优化递归使用循环方式代替递归从而避免栈溢出的情况以此提高程序性能。 fun main(args: ArrayString) {print(factorial(5)) // result120
}// 求 i 的阶乘 i * i-1
tailrec fun factorial(i: Int): Int {if (i ! 1) {return i * factorial(i - 1)} else {return i}
}扩展函数 把接收者类型放到即将添加的函数前面通过类可以对其调用的函数称为扩展函数。 如图所示接收者类型写在了test函数的前边在扩展函数里使用this引用的是接收者l类型的对象而非当前Test类。 如下图所示通过String类型还无法调用test函数我们在函数名称的前面加上String类型再次通过类型就可以引用test函数。 扩展函数只能由接收者类型调用不能通过扩展函数所在的类调用。 扩展函数不仅可以扩展函数还可以扩展属性。
val String.lastIndex: Intget() 0fun main(args: ArrayString) {var a aaaa000print(a.lastIndex) // result0
}小知识
扩展函数不可以重写。扩展函数实质上是静态函数。在扩展函数里使用this引用的是扩展函数的类型而不是函数当前所在类。如果扩展函数在其接收者类型之外声明则它不能访问接收者private或protected成员。扩展函数、属性的代码优先级高于接收者类型原有的函数、对象等同于重写接收者的同名函数、属性。
高阶函数 高阶函数是将函数作为参数或返回函数的函数。 在以下的示例代码中testFun1(i: Int, face: (String) - String)就是一个高阶函数因为它接受一个函数值作为它的第二个参数。
fun main(args: ArrayString) {testFun1(1) { it -// 实现业务逻辑将it给return回去it}
}// face: (String) - String 等于 调用高阶函数使用的名称: (需要传递的参数类型) - 返回类型
fun testFun1(i: Int, face: (String) - String) {// 接收到face返回值并将其printval value face(testFun)println(testFun$value)
}在实现高阶函数的lambda表达式里如图main函数对testFun1的调用若设置有返回值则默认return最后一行的代码结果若不设置则返回Unit。
运行结果 高阶函数除了上述使用方式之外还可以根据需求传入不同函数对逻辑进行处理。
fun main(args: ArrayString) {val plusResult num1AndNum2(20, 30) { n1: Int, n2: Int -// 执行运算 n1 n2}println($plusResult) // result:50val 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)
}除此之外高阶函数的lambda表达式还可以拆分成lambda表达式变量、匿名函数分别以变量、匿名函数的形式传入高阶函数。
fun main(args: ArrayString) {val minusResult num1AndNum2(20, 30, lambda1)println($minusResult) // result-10val aaa num1AndNum2(20, 30, lambda2) // result50println($aaa)val anonymous1 num1AndNum2(20, 30, a) // result-10val anonymous2 num1AndNum2(20, 30, b) // result50// 匿名函数num1AndNum2(20,30,fun(x,y) x y) // result50
}
// 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
inline lambda表达式在底层被转换成了匿名内部类的实现方式每调用一次 lambda 表达式都会创建一个新的匿名类实例会造成额外的内存和性能开销但这种开销可以通过inline lambda表达式来消除。使用inline关键字修饰的函数也被称为内联函数。 使用inline关键字需结合反编译才能看见效果。
fun inlineFun(action: (() - Unit)){println(inlineFun: 调用前...)action()println(inlineFun: 调用后...)
}fun main(args: ArrayString) {inlineFun {println(inlineFun: 正在调用...)}
}使用Idea、Android Studio开发工具反编译代码步骤在开发工具的顶部菜单栏 Tools Kotlin Show Kotlin Bytecodes Decompile。 inlineFun未添加inline函数代码反编译结果 inlineFun添加inline函数代码反编译结果 通过两次添加inline关键字前后的反编译比对可以看出inline函数是将表达式转移到调用方通过这样的方式减少lambda创建新匿名类造成的开销。
当你尝试在没有lambda表达式的函数上使用inline时编译器则会提示inline对性能预期影响微不足道应该结合高阶函数一起使用的警告。 Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types. 需要注意的是inline函数不支持修饰变量包含持有高阶函数的变量只能用于修饰高阶函数。
noinline 如果不希望传递给inline函数的所有lambda都被内联请使用修饰符noinline标记一些不需要inline的函数参数仅限lambda表达式。使用noinline的前提是使用的函数必须是inline函数。 inline fun inlineFun(noinline action: (() - Unit)){println(inlineFun: 调用前...)action()println(inlineFun: 调用后...)
}fun main(args: ArrayString) {inlineFun{println(inlineFun: 调用中...)}
}添加noinline代码反编译后“调用中…”的字符并没有被反编译出来而是用action$iv.invoke();调用函数。 crossinline crosinline用于禁止传递给内联函数的lambda中的非局部返回。 在内联函数的lambda表达式里使用return会中断高阶函数后面代码的执行讲inline函数有讲解到inline函数是将表达式转移到调用方因此下面代码的“调用后”是不会print。
inline fun inlineFun(action: (() - Unit)) {println(inlineFun: 调用前...)action()println(inlineFun: 调用后...)
}fun main(args: ArrayString) {inlineFun {println(inlineFun: 调用中...)return}
}使用crossinline关键字则可以杜绝这种情况。inline函数添加crossinline后return将会报return is not allowed here的错误。 部分博客把crossinline的含义解释成“检查代码中是否有return如果有则会执行不通过”。这样的说法是片面的官方文档已经解释了是禁止非局部返回。如果高阶函数里面的return不影响到嗲用高阶函数的函数后面代码执行是可以使用return如下 匿名函数 省略了函数名字的函数称之为匿名函数。 匿名函数往往结合高阶函数一起使用匿名函数默认隐式return最后一行代码写法有以下三种
val a fun(str: String): String aval b (fun(str: String): String b)参数和返回类型的指定方式与高阶函数相同如果可以从上下文中推断出参数类型则可以省略参数类型。如下
// 匿名函数
println(fun(item) c)
// 高阶函数
fun println(str : (String) - String){println(str(ttt))
}匿名函数与lambda表达式类似写法不同但执行效果可以是一致的如下lambda函数等同于上面的匿名函数写法
var d: (it: String) - String { d }var e: (String) - String { e }var f { it: String - f }看了好几篇的博客大部分都是说lambda表达式就是匿名函数我在kotlin官方文档找到的匿名函数写法并不包含lambda表达式同时也不知道其它作者所表达的lambda表达式就是匿名函数的依据来自于哪里。
在官方文档的匿名函数介绍里官方讲解了匿名函数与lambda表达式的区别lambda表达式和匿名函数之间的另一个区别是非本地返回的行为。没有标签的return语句总是从用fun关键字声明的函数返回。这意味着lambda表达式内的返回将从封闭函数返回而匿名函数内的返回则从匿名函数本身返回。
小结
将匿名函数作为参数传递时将它们放在括号内。允许您将函数放在括号外的速记语法仅适用于 lambda表达式。 参考文档 1、高阶函数 2、标准、作用域函数 3、Kotlin之高阶函数 4、《Kotlin从零到精通》—— 函数的运用 5、Android筑基Kotlin扩展函数详解——郭霖 6、Kotlin Doc——Extensions 7、Kotlin Keywords and operators