Java 8的Lambda及其在Android 开发中的应用

很久之前在盆友圈发了一张照片

lambda_example

上面的两段代码是完全等效的,但是代码行数从11行降低到了一行,更不用说在第一段代码里面,我在run方法的前后以及内部都没有加入任何的空行。由此可以看出,使用lambda可以让你的Java代码在某些情况下达到何等的简洁。
那么问题来了。。。

什么叫lambda呢?

Java 8 给我们带来了lambda,然而在Oracle的文档中,我没有找到lambda的定义,wikipedia里面也没有找到适合Java中的lambda的定义。写这篇文章的时候,我在这里 看到一篇很好的介绍lambda的文章,它里面给了一个定义,我觉得还挺合适的。

A lambda expression is a block of code with parameters.

lambda的写法

首先列举一个完整的lambda expression:

(int a, int b) -> {
    System.out.println("Performing add operation...");
    return a+b;
}

一个lambda expression由三部分组成:

  • 参数:(int a, int b)是这个lambda expression的参数部分,包括参数类型和参数名
  • 箭头:->
  • 代码块:就是用"{}"包含着的那两句代码。

上面说的是一个完整的lambda表达式,在很多情况下,很多东西是可以省略的。比如说,当系统可以根据context自动推断出参数的类型的时候,参数类型是可以神略的。这样的话就可以写成:

(a, b) -> {
    System.out.println("Performing add operation...");
    return a+b;
}

系统怎么自动推断出参数类型的呢? 在下面我们就可以看到。
再比如,如果只有一个参数,而参数的类型有可以自动判断,那么连“()”也是可以省略的,那么久写成了:

a -> {
    System.out.println("Performing add operation...");
    return a+a;
}

再再比如,如果代码块里面只有一行代码,那么“{}”也是可以省略的,那么久写成了:

a ->
    return a+a;

是的,可以写在同一行

a -> return a+a;

让我们更进一步,在这里,“return”其实也是没必要的。

a -> a+a;

Great, 如果没有参数的话,是不是就可以写成:

-> a+a

呢?
很可惜,如果没有参数,那么前面的"()"是必须存在的。也就是说,必须写成:

()-> a+a

lambda的用法

实际上,如果你直接把上面的代码放到你的编辑器里面,你的IDE是会报错的,因为lambda是不能这样使用的。lambda的使用永远要跟一个叫做Functional Interface的东西绑定在一起。什么叫Functional Interface呢?Functional Interface也是Java8 中引入的概念,是的,是为了lambda。我们知道java中的interface,而Functional Interface就是一个“只有一个抽象方法”的interface。比如Runnable 这个interface就只有一个run方法,那么它就是一个Functional Interface
那么或许你要问了,什么叫只有一个抽象方法的interface?interface中的方法不都是抽象的吗?Well,在java8以前是这样的,而在java8中,Java引进了default method的概念,就是带有具体实现的方法:

public interface DuckInterface {
    public void walksLikeADuck();
    public default void talksLikeADuck() {
        System.out.println("Quack!");
    }
}

在上面的例子中,talksLikeADuck就是一个default method,注意到这个方法的定义中有一个“default”修饰符。相信很多人会觉得这是个非常有用的feature,我也是这样觉得的。
再说回Functional Interfacel,lambda,刚刚说到,lambda必须和Functional Interface配套使用,那怎么配套使用呢?
以安卓里面的View.OnClickListener为例(它也是个Functional Interface)如果没有lambda,我们经常会这样使用的。

View.OnClickListener onClickListener = new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        handleClick();
    }
});
findViewById(R.id.someView).setOnClickListener(onClickListener);

或者直接使用匿名内部类:

findViewById(R.id.someView).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        handleClick();
    }
});

在上面的5行代码中,有用的其实只有handleClick(),而我们却必须用5行代码去处理,这是非常繁琐的。在Java 8以前的世界,这样的代码非常多。有一个专门的名词来称呼这种性质的代码,叫“boilerplate code”,我不知到中文叫什么。。。
现在好了,有了lambda,我们可以这样写:

View.OnClickListener onClickListener = view -> handleClick();
findViewById(R.id.someView).setOnClickListener(onClickListener);

匿名内部类的版本:

findViewById(R.id.someView).setOnClickListener(view -> handleClick());

是不是瞬间觉得简洁优雅了?
从上面的例子可以看到,lambda其实就相当于简化了Functional Interface的实例的创建。当然,从真正意义上来讲,lambda的意义不止这么一点点,只不过从使用的角度来看,你可以这样看待。

从lambda到Functional Programming

这里稍微讨论一下关于lambda的其他一些特性。
在上面的例子中

View.OnClickListener onClickListener = view -> handleClick();
findViewById(R.id.someView).setOnClickListener(onClickListener);

这里,你既可以吧onClickListener看作是OnClickListener的一个instance,也可以把它看做一个代码块,后面的那句findViewById(R.id.someView).setOnClickListener(onClickListener);就相当于是吧这个代码块传给了view.setOnClickListener()这个函数。也就是说,从某种意义上来讲,你可以把lambda看作是可以相互传递的代码块。而传递代码块,是Functional Programming(一下简称FP)非常重要的一个特征,虽然说这两者其实没有什么对等关系。因为FP的本质特征是,运行一段代码并不会改变事物的状态,也就是说,没有side-effect。而lambda里面是可以调用所在的类的成员方法的、也可以访问和修改所在类的成员变量的。
话说回来,关于FP我也不是了解的很多,我本身并没有多少FP的经验,虽然对Ruby有一定了解,但Ruby也只是“可以比较好的进行”FP而已,也不是纯粹的FP语言。纯粹的FP语言是List(包括Scheme,Clojure)、Haskell、ML等等这些。关于FP,Robert Fowler(就是《Clean code》和《The Clean Coder》的作者)有一个讲得很好的视频在这里
刚刚讲到,lambda的代码块可以访问所在类的成员变量和成员方法,那对于局部变量?
我们知道,方法内部定义的匿名类是可以访问所在方法的final局部变量的,作为Functional Interface的简写方式,lambda在这点上面跟匿名类保持了一致。也就是说,lambda可以访问定义它的那个方法的final局部变量。而在Java8里面,lambda还可以访问所谓“Effectively final”的局部变量。所谓“Effectively final”的局部变量,就是说除了在定义的时候给了一个初始值以为,在没有改变过她的值的那些局部变量:

int age = 26;   //在这里,age就是Effectively final的局部变量
Runnable r = () -> System.out.println("My age is "+age);
new Thread(r).start();

在Android开发中的应用

可是!!!Android只支持Java 7啊?怎么办?莫急,要相信网友的力量,已经有人开发了gradle的插件,可以将java 8中的labmda表达式在编译出来的bytecode里面给它转化成Java 7兼容的代码。猛戳这里,使用方法那个页面都用,在这里就不赘述了。

关注公众号“小创作” 及时获取最新文章


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

推荐阅读更多精彩内容