神奇的Lambda

来自:趣谈编程,作者:趣谈编程

多变的需求


一天傍晚,慧能把一尘叫到自己的身旁。


慧能

一尘啊,为师想知道咱们班哪些人身高170?你可以帮我写一个程序吗?

这还不简单?

一尘

只见一尘飞速的写下了下面的代码


涛声依旧注:Student 类有姓名、身高和体重等属性。


慧能

那我如果想知道身高大于165的同学呢?

那就复制一份刚才的代码然后把170改成165.

一尘



慧能

难道对于每一个身高都要这样重复的复制和修改吗?

这个。。。

一尘

慧能

提醒你一下:抽象



哦,我知道了,我可以用一个变量来代替这个你要求的身高值。

一尘

立刻一尘写下了如下代码



慧能

嗯嗯,不错,孺子可教也。

嘿嘿!

一尘

慧能

那我现在想找那些体重过百的同学怎么办呢?


这次一尘吸取上次的教训,直接用一个变量来表示体重了。只见一尘快速写下了如下代码。



慧能

那如果我想知道哪些同学考试前十呢?如果我想知道哪些同学的家在陕西呢?。。。

啊! 好多需求啊,这又得粘贴复制之前的方法,然后然后再稍微修改一下。

一尘

慧能

所以说你需要写出一个通用的方法,可以应对我的这些变化。

这可怎么搞,这不能再搞一个x吧。

一尘


只见师傅慢慢悠悠地说了两个字:


抽象


又是抽象,这个弟子不才,还请师傅指教。

一尘

慧能

Java中的多态是什么?接口又是什么?


既然你想对我不同的行为进行抽象,而方法代表着行为,那么你就需要用到抽象方法


你可以在一个接口中声明一个抽象方法,然后再不同的实现类中去实现这个方法。这样不就进行了统一了吗。

说着只见师傅写下了如下代码


然后上层在使用的时候可以这样使用



此时的抽象方法 test 的实现是由调用你写的通用方法 findEligibility 的人来实现的。

test方法的实现被放在了一个对象中了,这个对象是匿名的。也就是Java中的匿名类的实例。

这个对于Java开发者很是自然,test方法被放在了类里面,然后类的引用被传递到 findEligibility() 之中。这样就可以使用引用来调用方法了。

哦,对哦,可以定义接口,然后根据不同的需求进行不同的实现,而我写的方法却不用改动。这个接口就相当于抽象后的 x变量。

一尘

慧能

说的没错。

慧能

一尘,你有没有发现我刚才写的代码很啰嗦,代码模板很多。

嗯嗯,是啊,但是我们一直以来都是这样写的呀,难道还要更好的写法?

一尘


Lambda表达式


慧能

对,没错,在Java8中引入了Lambda表达式,我们可以使用它使得代码变得更加的简洁

我们首先看一下我们上面的代码的问题在哪里。

很明显的可以看出它比较笨重,占的空间比较大,编写起来也耗时(因为要写很多模板代码),并且不能够直接看出你想表达的东西(不够易读,很繁琐)

那么我们可不可以直接把方法中的代码(核心代码也就是 student.getHeight() > 170)直接传递给 findEligibility() 方法呢,在之前是不行的,因为我们的方法必须依附于类而进行传递。

但是在Java 8 中这个愿望可以实现了,Java8允许我们直接传递方法,而不用把方法放在类里面进行传递了。我们可以通过Lambda 表达式实现它。

那么我们应该如何用Lambda表达式实现它呢?我们可以这样写。

当你看到这样的改变后会想,这Lambda到底什么鬼?怎么这样写,但是对比一下和之前的写法,又感觉确实代码简洁了许多。

看不懂没关系,我们来解释一下这句Lambda表达式的意思吧。

首先是Lambda参数,细心的你可能已经发现了,这个参数就是 之前写的 test 方法的参数。

箭头把参数和主体分开来了

然后就是Lambda主体,其实就是test方法体里面的东西。

聪明的你可能已经发现,其实上面的Lambda表达式就是简化版的 test 方法,并且这个方法可以直接传递给 findEligibility() 方法,不用依附于某一个类或者对象上。

从演变过程来看,Lambda确实去掉了很多不必要的信息,保存了最核心的东西,这样一来,代码就会更接近你想表达的东西,也就更加简洁了。

在演变的第一步,我们让方法摆脱了对类的束缚,这一改变是巨大的。从此我们可以将方法块直接传递给方法中的参数了。

这样方法就已经脱离了类的存在而直接存在了。

逻辑严谨的同学可能也能够看出,这里的这个Lambda表达式,其实就是我们之前写的接口中的抽象方法的具体实现。

如果你的Lambda表达式不符合test方法的声明时,编译器就会报错,比如:

test只有一个参数。这里的Lambda有两个,与之不符。


函数式接口


还记得上面写的接口Predicate吗?

它就是一个函数式接口。

那什么是函数式接口呢?其实就是只含有一个抽象方法的接口就是函数式接口。

在新的API设计中,用注解@FunctionalInterface来表明某一个接口是函数式接口

函数式接口本质还是一个接口,它里面有一个抽象方法,规定了方法的行为特征。


其实在我们平时使用中,有很多情况我们会使用同样的接口,所以Java 8 设计者给我们提供好了几个常用的函数式接口,比如常见的 Predicate、Consumer和Function。

Predicate

这个函数式接口应该不用多说,其实和我们例子中的Predicate一样,它定义了一个 test的抽象方法,用来接受一个参数T,然后返回一个布尔值。

只不过Java8设计者这个Predicate支持了泛型。

Consumer

这个函数式接口接受一个泛型T,没有返回值,它想表达的意思就是当你需要执行一类操作时,这类操作接受一个参数,但是没有返回值,执行一系列操作就完事了,这类操作很适合Consumer。

比如说你接受一个int值,然后打印它,这时候你就可以使用Consumer。

Lambda控制的是行为,在这里也就是我要如何处理这个2

Function

Fuction 函数式接口声明了一个 apply 的方法,它接受一个泛型T,然后返回一个泛型R。当你需要接受某一个东西,并且还需要返回某个东西的时候可以使用Fuction.

比如你想实现输入一个字符串,返回一个字符串的长度,那么就可以这样。

原来Lambda这么强大

一尘

慧能

嗯嗯,灵活的使用它可以编写出优雅的代码。关于Lambda具体实战,以后再和你分享吧。


参考:

《Java8实战》

《码农翻身》

推荐↓↓↓
Java编程
上一篇:图解源码 | 接管SpringMVC的自动配置 下一篇:一个小例子秒懂ThreadLocal使用及原理