Kotlin学习总结(四)写个函数(下 )

273 阅读7分钟
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){
}
总结:这部分主要就是学习了函数这块,都属于函数里面比较难去理解的部分,需要多写,多实践。