Kotlin进阶篇:Lambda编程1,lambda表达式简单使用

前言

前面的几篇文章,我们学习了Kotlin类、接口、变量、函数、循环语句、条件语句等基础知识,这些基础知识已经足够让我们开始用Kotlin编程了,但总归还是带着Java面向对象编程的思想去编程,让我们在编程的时候,有种在用Kotlin写Java的感觉(如果你还没用Java8的前提下),可能会让不少人觉得Kotlin也就这样嘛,吹嘘的太厉害了,那么接下来的Kotlin进阶篇文章,将会带领大家深入的了解Kotlin,挖掘Kotlin函数式编程的知识点,给你带来不一样的感觉~

lambda表达式

什么是lambda ? lambda的本质其实就是可以传递给其他函数的一段代码,具体的语法结构如下图所示。


lambda表达式

lambda对于我们来说,用的最多的地方应该就用来替换匿名内部类,下面我们把view设置点击事件的代码用匿名内部类和lambda分别写一遍,看下区别在哪里。

//匿名内部类写法
textView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.d("tag", v.getId() + "点击事件");
    }
});

//lambda写法
textView.setOnClickListener { view ->
        Log.d("tag", "${view.id}点击事件")
}

可以看到,lambda相较于匿名内部类来说,写法上更简洁,可读性上也没有降低,而且就内存消耗而言,使用匿名内部类会显示的声明匿名对象,匿名对象每次调用时,都会生成一个新的变量,但lambda并不是,如果lambda表达式没有访问来自定义它的函数的变量,那么相应的匿名类实例是可以复用的,这个我们在下面的lambda在作用域中访问变量章节会说道。

lambda表达式在作为函数参数时,Kotlin有这么一个语法约定:
示例lambda表达式:

people.maxBy({ p: Person -> p.age })
  • 当lambda表达式是函数调用的最后一个实参时,它可以放到括号的外面
people.maxBy(){ p: Person -> p.age }
  • 当lambda表达式是函数唯一的实参时,空括号对可以省略
people.maxBy{ p: Person -> p.age }
  • 当lambda的参数的类型可以根据上下文被推导出来时,可以不需要显示的指定参数的类型,当你不确定参数类型是否可以推导出来时,可以遵循这个简单的规则:先不声明类型,等编译器报错后再指定类型
people.maxBy{ p -> p.age }
  • 当lambda只有一个参数,且参数类型可以根据上下文推导出来时,可以使用编译器生成的it参数名称来替代参数
people.maxBy{ it.age }
  • 当函数有多个lambda表达式参数时,建议使用正常语法书写,不建议把lambda放在外部,会降低可读性

lambda表达式变量

lambda表达式可以存储在一个变量中,把这个变量当作普通函数对待。

val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }  //完整语法,但通常我们会省略类型

val sum = { x: Int, y: Int -> x + y } 

调用时,把变量当成函数,通过传入参数调用即可sum(1,2)。此处涉及到函数类型,在这就不多聊了,后面我们说到高阶函数时,会详细的和大家聊聊。

lambda在作用域中访问变量

lambda在实际使用中,经常需要访问局部变量、全局变量、函数参数等,在Java8之前,如果我们要访问这些变量的话,都需要用final修饰符修饰之后才可以访问。在Java8之后取消了这个访问限制,即使没用final修饰也可以访问。但如果想修改这些变量的话,Java一般会声明一个单元素的数组,其中存储着要修改的变量,和Java不一样,Koltin则允许lambda内部访问非final变量和修改这些变量,我们称这些变量被lambda捕捉,看下代码。

// Java
private String x = "";
void test(String y) {
    final String[] z = {""};
    final String[] y1 = {y};
    textView.setOnClickListener(v -> {
        x = "";  //全局变量可以直接修改
        z[0] = "";  //局部变量需存储在final修饰的数组中,修改数组元素来达到修改的目的
        y1[0] = "a";  //形参需存储在final修饰的数组中,修改数组元素来达到修改的目的
    });
}

//Kotlin
private fun test(y: String) {
    var z = ""
    var y2 = y
    textView.setOnClickListener { view ->
       run {
            z = "a"  //局部变量可直接修改
            y = "b" //形参不可直接修改,是因为形参默认是val的即final的
            y2 = "b"
        }
    }
}

对于为什么Kotlin可以直接在匿名实例中修改局部变量,其实很简单,我们看下编译后的文件就知道了


image

在了解了lambda如何在Kotlin中访问变量,我们接下来再看下,被访问的变量的生命周期。
在默认情况下,我们都知道局部变量的生命周期是和声明它的函数的生命周期一起的,但如果它被lambda捕捉了,那么使用这个变量的lambda相关代码则被存储起来并稍后执行(比如点击事件,则是在点击的时候材才执行),所以这个变量的声明周期也就不在跟着函数一起了,而是跟着捕捉它的lambda一起。

//这个例子中,局部变量title的声明周期,在函数执行完毕后,就被释放结束了
private fun test() {
    val textView = TextView(this)
    val title: String = ""
    textView.setOnClickListener {
        run{
        }
    }
}

//这个例子中,title的声明周期则在点击事件之后,才会被释放结束
private fun test() {
    val textView = TextView(this)
    val title: String = ""
    textView.setOnClickListener {
        run {
            println("$title")
        }
    }
}

上面我们说过lambda相较于匿名内部类的优点,如果lambda没有捕捉变量的话,那么lambda创建的实例是可以复用的,但如果捕捉了变量,我们需要把这个变量和lambda一起存储起来使用,也就导致了我们每次使用lambda时,也需要创建一个新的实例了。

开篇我们说过lambda可以用来替换匿名内部类,之前的关于object关键字的文章,我们也说过object可以用来替换匿名内部类,那我们到底什么场景下用lambda表达式,什么场景下用object关键字呢?这里告诉大家,记住一个技巧,如果匿名内部类有多个待实现的方法时,选择使用object关键字,如果只有一个待实现方法,则使用lambda表达式,至于为什么一个待实现方法时建议用lambda而不是object?还记得前面我们刚说过的lambda相较于匿名内部类的内存消耗的优点吗?Bigo!。

好了,这篇文章我们主要了解了lambda的定义、语法规则、变量的捕捉等基础知识,希望能让你们对lambda有个基础的了解,下篇文章我们将学习lambda在集合中的实战应用,再见喽~

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