Lambda表达式以及AS 对其的支持

ps: Lambda 出来啦很久了,我最近才开始学习使用啊,这还是因为 DataBinding 调用带参数的方法需要用到 Lambda 表达式才开始学的。话说新东西出来的确是要学的,研究的。就算不是第一时间去学习研究,那也要在有人尝过鲜之后对齐有肯定的评价后,第一第二时间开始学习入手,要是都等到需要的时候再去学习,那真是一件很让让人沮丧的事。

再说一下,Lambda 刚出来时,因为的确就看着不习惯,也没去专门学习 Lambda ,这件事往小了说是偷懒了,往大了说就是没有勇气去跨出舒适区,没有勇气去跳出舒适区,怎么能快速的进步呢,要不怎么升职加薪,我最近也是体悟出了这个道理的含义。开发同修仙,不进则退,只有挑战自己向前走这一条路,也就是要勇于跳出舒适区。

先说 AS 如何支持 Lambda 表达式

  1. 首先确定你的AndroidStudio中使用的是jdk1.8的版本


    forMarkdown.png
  2. 在项目的根路径 build.gradle 文件中添加 Lambda 的脚本下载路径,也就是 classpath

classpath 'me.tatarka:gradle-retrolambda:3.2.5'
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
        classpath 'me.tatarka:gradle-retrolambda:3.2.5' // 添加的这行
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

重点再说一次,是在项目的根路径的 build.gradle 文件中添加,别加错地方

  1. 在你的module目录下,通畅也就是 app 目录下的 build.gradle 下添加 java 编译版本号
apply plugin: 'me.tatarka.retrolambda'

    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
apply plugin: 'me.tatarka.retrolambda'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"

    // 加的这块
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    defaultConfig {
        applicationId "ex.hxx.com.daggertest"
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
 
  .......
  1. 然后我们就可以看见在编译器在可使用 lambda 的位置会出现灰色提示,可以使用 ALT+ENTER 快捷键自动转换成 lambda 表达式



跟着上面操作过后,我们就可以在代码里面使用 Lambda 表达式了,那么 Lambda 表达式是什么呢

简单认识下 Lambda

最简单的说法,也是直指本质的是: Lambda 是 java 对方法中需要传入的 匿名实现类的简写。举个例子,我们给 button 注册一个点击事件,就是传入一个 View.ClickListener 对象。大伙都体验过在一个方法的参数中传入一个匿名实现类,一个还好,要是需要传入多个呢,那在查看时就是低于式体验了,太不友好了,这种体验类似于 callback 的回调地狱,我找个 JS 的例子大家欣赏下:

fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})

这个玩意写完了,就是让作者自己看都尼玛感觉是坨屎吧,有时候,这种情况避免不了,尤其是在网络,多线程,数据库代码中这种多层 callback 嵌套或是需要传入多个匿名实现对象的代码会有较多机会出现。Lambda 就是为了解决这种屎一样的代码的,效果还可以,但还是不够彻底,也就是从没法看,变成有法看了,但我们还是得学啊,因为目前也只能做到这样了,别人都这么写,到时你看不懂,看不习惯,看不爽就麻烦了。

上面简单的说了 Lambda 是干什么的,那么现在正式来说说 Lambda 了。Lambda 是 java 8 的特性, 是函数式编程的一个思想,是 java 在往函数式编程发展过程中加入的一大特性。

Lambda 在函数式编程中应该叫 闭包 ,我们平时也叫 Lambda 表达式,这里有一个 闭包 概念的解释,看不懂没关系,不影响我们使用他。

"定义在函数内部的函数", 在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

--来自阮一峰技术博客

对于函数式编程和 java 的对比,北京 GDG 2017中有一位就是专门的说的这块内容,值得一看,Kotlin as Your Next Language ,放心是中文的

看着很抽象是不,其实我们可以这样理解:使用 Lambda 表达式替代匿名实现类,Lambda 表达式最大的好处是简捷,尤其是对于内部只有一行逻辑实现的匿名实现类优化最好,但是注意啊,Lambda 只能代替内部有一个方法的匿名类,一般多用来替代只有一个方法的接口和内部类,比如各种 ClickListener,runnable 接口。

好了我们先看一下 Lambda 表达式的优势,使用 button 的点击事件做个例子:

原生版

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText( MainActivity.this,"AAA",Toast.LENGTH_SHORT ).show();
            }
        });

Lambda 版

 button.setOnClickListener(v -> Toast.makeText( MainActivity.this,"AAA",Toast.LENGTH_SHORT ).show());

这样我们把2层多行代码通过 Lambda 表达式优化成一层单行代码,这样阅读起来的的确确是好太多了。缺点是必须要适应,熟悉 Lambda 表达式的写法,转变起来需要一些时间。

Lambda 语法

Lambda 的语法是隐藏实现类的 类名方法名,下面是基本写法:

(parameters) -> { expression or statements }

左边括号内是方法需要传入的参数,右边括号内是方法实现逻辑

下面是一些例子:

// 无参数, 返回1+2的结果 
() -> 1+2  
  
// 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x
  
// 接收2个参数(数字),返回表达式运算的结果 
(x, y) -> x + y

// 多个语句要用大括号包裹, 并且返回值要用return指明
(x, y) -> {
    int result = x + y;
    System.out.print(result);
    return result;
}
  
// 接收string 对象, 并在控制台打印 
s -> System.out.print(s)
其中参数的类型可以不声明, 编译器会结合上下文智能推断, 比如这句

s -> System.out.print(s)
等价于

(String s) -> System.out.print(s)
注意: 无参数时()不能省略

语法非常简单, 就是因为简单, 反而更让人摸不着头脑。但是注意啊,Lambda 的语法的确是隐藏实现类的 类名方法名,但是这是对我们编码人员来说,对于机器来说还是要能准备的知道实现类类型和方法名,对于实现类类型,这个是由外层调用者的规定来实现的,对于方法名来说,就是通过限定实现类内部只有一个方法来实现的,要是有多个方法的话,编译器知道你调的是哪个方法,这里是在我们做代码封装,框架设计时需要注意的。

可能上面语法写的有些简略,可能会给大家带来一些困扰,那么下买我再来一个 Lambda 的例子:

标准 java 代码:

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
}); 

这是一段很简单的排序代码,但是涉及到一个Java匿名内部类的书写之痛,排序方法真正核心的是compare方法内的实现,但是为了实现b.compareTo(a),我们写了很多不必要的代码,lambda表达式正是这种类型代码的救星。

标准 lambda 表达式:

Collections.sort( names, (String a, String b) -> {
    return b.compareTo(a);
} );  

简略 lambda 表达式 1:

// 方法实现要是单行,我们可以省略 return,缩进为一行
Collections.sort( names, (String a, String b) -> {b.compareTo(a);} );

简略 lambda 表达式 2:

// 方法实现要是单行,我们还可以省去大括号
Collections.sort( names, (String a, String b) -> b.compareTo(a) );

简略 lambda 表达式 3:

// JAVA8 可以根据上下文直接推导出参数类型,我们还可以去掉参数类型
Collections.sort(names, (a,b) -> b.compareTo(a)); 

最终:我们的代码由原来的七行代码变为一行代码,初次感受可能感觉可读性降低了,不好看,但若是大家熟悉了 lambda 的写法,你会发现这样比之前容易阅读的多。

我们直接使用 lambda 获取匿名实现类对象也是可以的:

Comparator<String> comparator = (String s1, String s2) -> {
    doSomeWork();
    return result;
};
我再举几个 lambda 的应用场景:
  1. 单实现的接口
  2. 条目点击事件
  3. 封装网络请求的回调
  4. 与RxJava的链式调用
    1.view.setOnClickListener(v -> {
            //dosomething
        });

     2.  listview.setOnItemClickListener((parent, view, position, id) -> {
            //dosomething
        });  

     3.例如通过封装OkHttp或者Retrofit的回调方法,将其转变成单实现的回调接口进行调用  

     4.Integer[] list = {1,2,3,4,5};
Observable
    .from(list)
    .filter(integer->integer%2==0)//挑选出偶数
    .map(integer -> "number is"+integer)//转换成String
    .subscribe(s->System.out.println(s));//相当于forEach(s->System.out.println(s));
    //forEach是同步的 subscribe是异步的

最后

写到这里 lambda 就可以了,但是有几点还是要说一下的:

  • lambda 只使用于 内部只有一个 方法 接口 ,方法多了编译器会识别不出来你想调用那么方法,因为 lambda 把方法名省略了
  • 若要使用 lambda ,在代码封装和框架设计时要注意 lambda 省略 类名和方法名的特性
  • Lambda的作用域:
    在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量(在访问外层局部变量时虽然没有添加final关键字,但是我们可以理解为该变量为隐形final),或者实例的字段以及静态变量。即局部变量在lambda表达式中只可读不可写,而成员变量和静态变量既可读又可写;
  • this 关键字:
    在匿名内部类中,this 关键字指向的是匿名类本身的对象,而在 lambda 中,this 指向的是 lambda 表达式的外部类。
  • 方法数差异
    当前 Android Studio 对 Java 8 新特性编译时采用脱糖(desugar)处理,lambda 表达式经过编译器编译后,每一个 lambda 表达式都会增加 1~2 个方法数。而 Android 应用的方法数不能超过 65536 个。虽然一般应用较难触发,但仍需注意。

参考资料:

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