2019.07.04生活源于心态,戒骄戒躁
前面部分其实,已经写过函数了,但是都是一些基本的语法,接下来可能就稍微需要花时间去理解一下了。
Lambda 表达式
“Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
在Java中已经被广泛的运用,但是也是在Java8的时候才支持Lambda表达式。Lambda表达式的本质其实是匿名函数,因为在其底层实现中还是通过匿名函数来实现的。但是我们在用的时候不必关心起底层实现。不过Lambda的出现确实是减少了代码量的编写,同时也是代码变得更加简洁明了。
sample_text.setOnClickListener(object : View.OnClickListener{
override fun onClick(v: View?) {
//........
}
})
//使用Lambda表达式
//it:View!
sample_text.setOnClickListener {
//........
}要习惯用,kotlin的代码很多都是用的这样的Lambda表达式,确实是代码要简洁多了,但是可读性,说真的还是差点点。
构成
- Lambda表达式总是被大括号括着
- 其参数(如果存在)在 -> 之前声明(参数类型可以省略)
- 函数体(如果存在)在 -> 后面。
val sum = { x: Int, y: Int -> x + y }如果我们把所有可选标注都留下,看起来如下:
val sum: (Int, Int) -> Int = { x, y -> x + y }将 lambda 表达式传给最后一个参数,在 Kotlin 中有一个约定:如果函数的最后一个参数接受函数,那么作为相应参数传入的 lambda 表达式可以放在圆括号之外
val product = items.fold(1) { acc, e -> acc * e }写法
无参数写法
//普通写法
fun hello() {
//...代码块...
}
// lambda代码
val hello = {
//...代码块...
}有参数写法,如果返回值类型可以根据操作的代码自推导出来的,可以省略返回值类型
//普通写法
fun hello(num : Int ,str: String) : String{
//...代码块...
return str+num
}
// lambda
val hello : (Int , String) -> String = {num , str -> str+num}
//或者
val hello ={num:Int ,str:String -> str+num}把lambda表达式当做函数中的参数。
open class HelloPeiqiTest{
init {
test()
}
//普通写法
fun helloPeiqi(num : Int ,str: String) : String{
//...代码块...
return str+num
}
//普通写法
fun helloTom(str1: String ,str: String) : String{
//...代码块...
return str1+str
}
// lambda
fun handleIntAndStr(num : Int ,str2:(str1:String ,str:String )->String) : String{
return str2.invoke("nihao","woshi")+num
}
//调用
fun test(){
//普通调用
var normalStr= helloPeiqi(3,helloTom("ni","hao"))
// lambda调用
var lambdaStr1= handleIntAndStr(12,{tempStr:String,tempStr1:String->tempStr+tempStr1})
// lambda调用 第二种放在括号外面的写法
var lambdaStr2= handleIntAndStr(12){tempStr:String,tempStr1:String->"haha"+tempStr+tempStr1}
println(normalStr)
println(lambdaStr1)
println(lambdaStr2)
}
}it并不是Kotlin中的一个关键字(保留字)。
it是在当一个高阶函数中Lambda表达式的参数只有一个的时候可以使用it来使用此参数。it可表示为单个参数的隐式名称,是Kotlin语言约定的。
从 lambda 表达式中返回一个值
我们可以使用限定的返回语法从 lambda 显式返回一个值。 否则,将隐式返回最后一个表达式的值。
因此,以下两个片段是等价的:
ints.filter {
val shouldFilter = it > 0
shouldFilter
}
ints.filter {
val shouldFilter = it > 0
return@filter shouldFilter
}下划线用于未使用的变量(自 1.1 起)
如果 lambda 表达式的参数未使用,那么可以用下划线取代其名称:
map.forEach { _, value -> println("$value!") }匿名函数
匿名函数--就是没有函数名的函数。 lambda 表达式语法缺少的一个东西是指定函数的返回类型的能力。在大多数情况下,这是不必要的。因为返回类型可以自动推断出来。然而,如果确实需要显式指定,可以使用另一种语法: 匿名函数
fun(x: Int, y: Int): Int = x + y匿名函数看起来非常像一个常规函数声明,除了其名称省略了。其函数体可以是表达式(如上所示)或代码块:
fun(x: Int, y: Int): Int {
return x + y
}Lambda表达式与匿名函数之间的另一个区别是非局部返回的行为。一个不带标签的 return 语句总是在用 fun 关键字声明的函数中返回。这意味着 lambda 表达式中的 return 将从包含它的函数返回,而匿名函数中的 return 将从匿名函数自身返回。
闭包
所谓闭包,即是函数中包含函数,这里的函数我们可以包含(Lambda表达式,匿名函数,局部函数,对象表达式)。我们熟知,函数式编程是现在和未来良好的一种编程趋势。故而Kotlin也有这一个特性。
public class TestJava{
private void test(){
private void test(){ // 错误,因为Java中不支持函数包含函数
}
}
private void test1(){} // 正确,Java中的函数只能包含在对象中+
}
fun test1(){
fun test2(){ // 正确,因为Kotlin中可以函数嵌套函数
}
}高阶函数
Kotlin 函数都是头等的,这意味着它们可以存储在变量与数据结构中、作为参数传递给其他高阶函数以及从其他高阶函数返回。可以像操作任何其他非函数值一样操作函数。
为促成这点,作为一门静态类型编程语言的 Kotlin 使用一系列函数类型来表示函数并提供一组特定的语言结构,例如 lambda 表达式。
高阶函数是将函数用作参数或返回值的函数。使用lambda作为 参数或者返回值。高阶函数有助于简化代码,去除代码重复,以及构建漂亮的抽象概念。
open class HelloPeiqiTest{
init {
test()
}
// lambda
fun handleIntAndStr(num : Int ,str2:(str1:String ,str:String )->String) : String{
return str2.invoke("nihao","woshi")+num
}
//调用
fun test(){
// lambda调用
var lambdaStr1= handleIntAndStr(12,{tempStr:String,tempStr1:String->tempStr+tempStr1})
// lambda调用 第二种放在括号外面的写法
var lambdaStr2= handleIntAndStr(12){tempStr:String,tempStr1:String->"haha"+tempStr+tempStr1}
println(lambdaStr1)
println(lambdaStr2)
}
}内联函数
使用高阶函数会带来一些运行时的效率损失:每一个函数都是一个对象,并且会捕获一个闭包。 即那些在函数体内会访问到的变量。 内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销。
但是在许多情况下通过内联化 lambda 表达式可以消除这类的开销。
内联函数其实质:就是将形参数据类型的为***函数类型***的函数代码复制到执行函数中,而不存在函数调用;
使用"inline"关键字修饰函数,该函数的函数类型的形参如下:
inline fun fn(foo:(Int)->String){
foo(1)
}
fun foo(a:Int):String{
return "leslie"
}
调用:fn(::foo)如果没有不是inline函数,正常执行流程是:
- 保护fn现场
- 进入foo函数或者lambda表达式地址的执行foo函数
- 执行完后,恢复现场
但是使用内联函数:就不会进入函数地址去执行上面的第二步,而是将该函数或者lambda表达式的代码拷贝到函数中,此时在lambda表达式使用return语句就相当于返回其所在函数;
这时如果在lambda表达式中使用return语句,下面我们会讲到lambda表达式的return语句返回的是他所在函数,因此此时在lambda表达式使用return语句将会报错;
使用内联函数情形:当被调用的lambda表达式或函数包含简单的执行代码,就是用内联函数提升性能,否则,就不建议使用内联函数;
禁止部分内联:如果函数中有几个函数类型的形参,有的函数类型形参如果你不想使用内联,可以在形参前面使用noinline修饰形参.
inline fun test(fn:(Int)->Int,noinline fn2:()->Boolean){
}总结:这部分主要就是学习了函数这块,都属于函数里面比较难去理解的部分,需要多写,多实践。