Lambda表达式
先写写背景和最基本的东东,泛型加入和各种循环的复杂模式后面再慢慢深入。
需要看JDK8的背景
虽然网上的介绍很多,但是不如自己读一下document后来得正宗。
说一下缘由,突发的这个项目客户貌似是个暴发户,发疯什么都要用最新的,JDK8是否稳定也不管,各种要求最新。Lambda语法全上,各种jdk8最新的东西全往上搞,我靠。。。WS还有其他的容器是否对8的支持很好也不知道。。。。不过,这也不是坏事,学呗~~~~ 等jdk出到12的时候再回头看看也挺有意思。
本文也是以JDK8的tutorial为原本进行的翻译+自己理解的一些comment,有问题的话随时交流。
Lambda在.Net中的应用
本来是要去一个.Net项目做一做,但是我实在是没有兴趣,对windows的东西有些不感冒,还是做回了java。Lambda在.Net是有应用的,但是sorry我不太懂,上一次的.Net项目还是大学毕业的毕设,所以按照百度上面的文献来讲,”Lambda它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型”就是如此草草的干白的解释。。。这点就不多说了。
需要提前知道的一些概念
匿名内部类
一个内部类,实现了某一个接口。
比如,我有一个interface:
public interface CheckPerson {
boolean test(Person p);
}
当我需要在某一个地方做一个实现这个接口的实例,可以采用下面的写法,
CheckPerson tester = new CheckPerson() {
public boolean test(Person p) {
return true;
}
};
Interface中的default Method
不得不吐槽。十年河东十年河西。
之前的项目代码写死,都是hard coding。然后慢慢从Struts开始xml和ini的配置风潮开始涌动,spring的2.5前把配置做到的极限,当大家都沉浸在一个系统80%都在配置xml的快感一直不得高潮的时候,annotation和javaconfig刮来,各种硬代码的编入又回到了以前,最新的项目中见不到xml见不到配置,所有又重新变回了代码。虽然技术和思想革新了,但是这个1-2-1的过程也体现出了开发面向的粒度其实更细碎了,我们需要的是一种在大并行开发中每一个人的力量+在一起可以横向扩充的效果,而不再考虑一个总体的调度来看清方向来把握全局。所以,这算技术流派的获胜么?因为一群烂程序员坐在一起用这种方式出来的东西估计会弄死几个review的人,但是如果是技术很好的群体这种模式真的是很不错。
废话少说。
JDK8开始,在interface中加入了default Method的一种全新声明方式。
说白了就是,原来的interface只能声明,无法实现,实现都是implementation的事情。想一个场景,interface在当你发现所有实现这个接口的class们都需要一个共同的方法处理的时候,咋办?老版本的java是interface中加入一个声明,各个地方去实现;或者,我们采用一个abstract的类做一个适配器,abstract的类去实现部分的interface,让后让原来的implementation的class们都重新继承这个abstract的class。后者的做法有个问题,如果我们采用的是java的Proxy代理模式去动态生成继承类的话,所有的实现interface的class变成了继承一个abstract的class,Proxy宣告无法使用,当然采用非Java自己的Proxy模式(如cglib)是没有问题,但是的确各种改动量很大,也有一定的局限性。
OK,从JDK8开始,当你觉得上面是个大问题的时候,那么default Method的确可以帮助你:
public interface CheckPerson {
boolean test(Person p);
default void wtfaboutJDK8() {
System.out.println("nothing");
}
}
如上面的wtfaboutJDK8这个方法,加入了default的的关键字,成为了一个可以进行内容填充的方法。JDK8开始告别了interface只有接口没有实现的历史。
总觉得这个功能是java系统这么多年需要维护的成本有点高,oracle给大家个补丁的赶脚。。
Interface中的static Method
这个其实和default method差不多,也是在interface中可以定义一个带有实现的方法体。但是,这个方法提是一个static的,这个方法属于class不属于任何一个实例(和java中的static的修饰一样)。
看一个官网的例子即可:
public interface TimeClient {
// ...
static public ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString +
"; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default public ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}
这个getZoneId 的方法即是一个static的方法。
有了default method和static method后,java的interface应该叫啥?“全能接口”比较合适吧?呵呵。
Lambda的表达式
Lambda的表达式的建立基础:functional interface(功能接口)
各种神奇词汇层出不穷。
功能接口是个虾米东东。
先看看java给的解释:
A functional interface is any interface that contains only one abstract method. (A functional interface may contain one or more default methods or static methods.)
功能接口即一个interface仅仅含有一个抽象的方法。但是呢,可以包括一个或者多个default方法以及static方法。这就是为啥我上面要先写default方法和static方法的
OK,到这里有些人可能晕了,interface中的方法为啥有抽象方法?啥意思?貌似我们不写abstract关键字啊。好,看看java的解释,回忆回忆吧,估计好多人都忘记了。
Note: Methods in an interface (see the Interfaces section) that are not declared as default or static are implicitly abstract, so the abstract modifier is not used with interface methods. (It can be used, but it is unnecessary.)
默认的非default和static(上面那两个四不像的货)的方法都是abstract的哦,只不过我们没有必要写罢了。
Lambda的表达式的演变过程
按照java的document的顺序解释一下
简单的特征描述和check
背景我觉得没啥必要写,直接看代码吧
public static void printPersonsOlderThan(List<Person> roster, int age) {
for (Person p : roster) {
if (p.getAge() >= age) {
p.printPerson();
}
}
}
这段代码很简单,我们循环一个roster的List,然后取出里面的Person的元素进行一个关于年龄的check。嗯,oracle的工程师们看到后说,OK,你写的这段程序很脆弱(汗。。。(-__-)b),哪天你的getAge()啥的发生了变化,比如一些类型变化或者算法变化,导致你这个age的check不可用了咋办。也就是说,牵一发这里有可能会要改。好,我们改一改代码:
相对泛化的特征描述和check
public static void printPersonsWithinAgeRange(
List<Person> roster, int low, int high) {
for (Person p : roster) {
if (low <= p.getAge() && p.getAge() < high) {
p.printPerson();
}
}
}
OK,给一个范围吧。其实还是换汤不换药,只不过check写的稍微条件多了点。看不上这段代码的人依然看不上。多的不说了,继续。
独立出逻辑的class提取
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
我们把这个check放到了一个class 叫做tester中,这个tester实现了一个CheckPerson的接口。
接口的定义如下:
interface CheckPerson {
boolean test(Person p);
}
然后呢,我们要做一个实现这个接口的类:
class CheckPersonEligibleForSelectiveService implements CheckPerson {
public boolean test(Person p) {
return p.gender == Person.Sex.MALE &&
p.getAge() >= 18 &&
p.getAge() <= 25;
}
}
好,接口,实现者,调用者都有了,找个地方给注入进去即可,比如spring的di啥的。或者我们在调用的地方直接new:
printPersons(
roster, new CheckPersonEligibleForSelectiveService());
有的人又看不惯了,你这个多写了一个接口,还多写了一个实现类,麻烦啊,浪费啊。。。8啦8啦。。。贱人矫情没办法。来吧,继续改:
独立出逻辑的class使用匿名内部类
printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
可以了么?大哥。提取interface了,不适用硬check逻辑了,为了节省还是用匿名内部类了。您满意了么?
嗯。。。这时候人家说话了,看起来有点乱啊。(◎﹏◎)。。。(悟空,削死这个丑八怪!!(●’? ‘●))
Lambda表达式登场
其实,这个表达式真的那么好么?嗯。见仁见智吧。的确是节省了不少事情和空间,但是脑子其实还要费的。
// 我们声明的方法本身
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
........
// 我们需要调用的地方
printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
这是个虾米意思呢。。。猛一看觉得,我擦,回到了指针或者各种左右移位的状态了似的。
先从基本的说起。
前面提到了functional interface(功能接口) 还记得吧?
我们的这个CheckPerson接口中只有一个(抽象)方法test。符合Lambda表达式的前提,OK可以使用。
用最白话来解释就是:
Lambda表达式即functional interface接口中的唯一的abstract方法的一个代码流的动态展现。看下面这段代码,首先,指明了这个方法的参数Person p。
然后使用这个p做一系列的逻辑处理,这部分处理即我们指定的Lambda表达式。
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
java的Runtime时可以去评估你的Lambda表达式,然后return这个结果,所以你不写return的时候默认是return你的表达式的value的。
当然,你也可以显示地写出来return:
p -> {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
嗯,写了这么多其实就最后一点点有效,前面都是铺垫。刚开始理解起来可能有点困难,后面还会泛型化就更会乱,所以先理解理解。大的思路就是对于functional interface的实例对象,不用简化匿名内部类的方式,而采用更为简单的代码片段的传输思想来解决。
后面继续。
つづく???