多变的需求
一天傍晚,慧能把一尘叫到自己的身旁。
慧能
一尘啊,为师想知道咱们班哪些人身高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实战》
《码农翻身》
千千万万的公众号中
能被你识别都是缘分