Kotlin Lambda表达式、高阶函数 看这篇就够了

Lambda表达式

不少人接触lambda表达式是从Java8开始的


//匿名内部类写法

view.setOnClickListener(new OnClickListener() {

  @Override

  public void onClick(View v) {

  }

});

//lambda写法

view.setOnClickListener(v -> {

});

很多人在java里用lambda表达式都是借助IDE的代码补全功能,如果离开IDE的代码补全是不会想去用它的,因为lambda不能在代码结构上改变什么,它仅仅是简化了匿名内部类的写法,让我们不至于看到代码里的过多缩进。

而Kotlin的lambda则不同于java,其是有实实在在的意义的。lambda表达式在kotlin里可以看做是一个匿名函数,其描述了函数的输入输出类型。在kotlin里lambda一般是当作方法的一个参数使用,而在java里函数是不能作为参数的,这是两者的区别。如果一个函数的参数或返回值是另一个函数,那我们称这种函数为高阶函数。

高阶函数 Higher-order function

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入

  • 输出一个函数

以上是高阶函数的数学定义,在java中是没有高阶函数这种定义的,但是java要实现类似功能也不是不可以。比如一个函数要动态接受另一个函数在java中可以通过接口实现,比如:


public interface OnClickListener {

  void onClick(View v);

}

view.setOnClickListener(new OnClickListener() {

  public void onClick(View v) {

  }

});

这个代码大家应该都不陌生,因为java的函数参数只能是类或接口,所以只能把要执行的方法包在一个接口里然后在执行接口的onClick()方法。但其实我们真正要执行的仅仅是onClick的方法本身,只是因为java不支持方法作为参数,所以代码才不得不以这样的形式设计。

高阶函数代码表现

如果函数支持函数作为参数,代码应该怎么写呢?这样:


Fun fun1

view.setOnClickListener(funParam:Fun,paramB :Int){

    fun1 = funParm

}

...

fun(parmA,paramB...)

...

大概就是这样的一个形式,以上代码属于任何语言,只是表达了如果函数作为参数代码大概是个怎么样的形式。就是把方法作为一个方法对象,这个对象跟其他对象一样被调用,这样是不是比包装接口方便了?但是这个写法比较简陋,实际语言的语法肯定不是这样。首先方法肯定是无法定义为一个类型的,因为可以定义无数种。不过要确定某一个方法类型很简单,只要确定方法的参数和返回值类型完全一样,那么我们就认定这样的方法是同一个类型。在kotlin里是这样定义一个方法类型的。


(param1,param2,...)-> returnType

比如:(String,Int) -> Int 

//这定义了一个参数为String和Int类型,返回为Int类型的一个函数,所以符合此规则的都认定是同一个函数类型

那么在kotlin里的表现是什么样呢?


class View {

  ...

  //方法定义了一个参数为View返回类型为空的一个函数类型参数

  fun setOnClickListener(clickFun: (View) -> Unit) {

    clickFun(this)

...

  }

  ...

}

fun test{

  val clickFun = ::clickFunction //声明一个函数类型对象

  view.setOnClickListener(clickFun)

}

//这个方法的参数是View返回类型为空,所以这个方法可以用来声明setOnClickListener的参数类型

fun clickFunction(view: View) {

...

}

这种写法跟我们调用普通方法区别其实也不是很大,主要有两种区别:

  • 参数类型:普通参数类型一般是一个类对象,一般由一个或多个单词组成,比较简单如String,StringFormatter,函数类型参数则由两部分组成,(入参类型)->返回类型,如(View) -> Unit,稍微复杂

  • 对象声明:java对象声明是new 类名(构造参数) kotlin省掉了new,直接是类名(构造参数);函数类型的声明则是用“::”,双冒号在kotlin的官方称为“Function Refrence,用双冒号声明的对象则为函数类型对象。

  • 调用:对象调用方法一般都是Object.method(),而函数类型对象本身就是方法了,也就不能用这种方式了。其执行方式有两种 :Function(param) 或 Function.invok(param)

注意:kotlin的函数也是不能作为参数传递的,能作为参数传递的一定是对象,所以我们这里的参数是"::clickFunction"这个函数类型对象,而不是函数本身。

匿名函数

java的匿名内部类大家肯定都用过,用起来比较方便,在读代码时可读性比较强。而kotlin的函数也有对应的匿名函数写法,比如:


val clickFun = fun clickFunction(view: View) {}

这样写对么?由于这是匿名函数,所以原先的函数名就没有意义了,所以上面的写法是不对的,需要去掉原先函数名。


val clickFun = fun (view: View) {}

所以之前的调用方式也可以改成下面这样


fun test{

  //val clickFun = ::clickFunction //声明一个函数类型对象

  view.setOnClickListener(fun (view: View) {

    ...

  })

}

匿名内部类是一个函数类型对象,它不是函数,其在类的方法体里是不能直接调用的,一定是通过对象的引用来调用,所以匿名函数才能直接作为参数传递。

不是说Lambda么,怎么说了半天函数?函数跟Lamda有关系么?别急,下面就来解答。

Lambda是什么

Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数

以上是百科的定义,所以在kotlin里,Lambda表达式可以看作是匿名函数的表现形式,且大多数时候写法更简单。比如上面的匿名函数写法可以这样写:


view.setOnClickListener({ v:View ->  })

//如果lambda是函数最后一个参数,可以把lambda写到括号外面

view.setOnClickListener(){ v:View ->  }

//如果lambda是函数唯一的参数,可以把括号去掉

view.setOnClickListener{ v:View ->  }

//如果lambda是单参数,那么这个参数也可以去掉

view.setOnClickListener{ it.visbility = View.GONE}

参数如果省略不写,那要用的时候怎么办呢?kotlin对只有单参数的lambda统一使用"it"作为变量名。

这时候不少人就要问了,lambda省略这么多东西是怎么做到正确执行的呢。其实很好理解,因为在函数定义的地方已经声明好相应参数类型,所以kotlin从代码上下文推倒出了相关逻辑。

Lambda语法

语法基本有以下几种:


//声明lambda变量

val lambda : (param:Type,...) ->Type

val lambda = {} //无参数

val lambda = {a:Int,b:Int ->} //有参数初始化,语法同kotlin所有对象初始化声明

Lambda表达式在多参数方法中的应用

我们在定义高阶函数时,尽量吧函数类型参数放在最后一个,因为如果函数类型参数是最后一个参数时,可以把lambda写到括号外面


fun fun1(param:Int,clickFun: (String,Int) -> Unit) {

    clickFun("this",0)

}

//因为lambda是最后一个参数,所以lambda可以放在方法括号之外

fun1(0){p1,p2 ->

    println("$p1 $p2")

}

如果lambda不只是最后一个参数是这样的


fun fun1(clickFun: (String,Int) -> Unit,param:Int) {

    clickFun("this",0)

}

fun1({p1,p2->

    println("$p1 $p2")

},0)

lambda不是最后一个参数,那么它要写在函数括号里面

总结

其实Lambda没有想象中的那么复杂,我们只要了解其本质就能很好的在我们的代码逻辑中运用。在kotlin中如果能熟悉运用Lambda表达式,就能替代java中接口回调式的代码结构。我们的代码会变得很清爽,可读性会增强不少。

你可能感兴趣的:(Kotlin Lambda表达式、高阶函数 看这篇就够了)