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)

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

匿名函数

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中接口回调式的代码结构。我们的代码会变得很清爽,可读性会增强不少。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容