什么东西会了的话就很简单,对于不会的人永远就是非常难。
之前我一直说Lambda太反人类,这玩意儿真难,淦。
之前跟海螺dalao和仓鼠dalao一直吐槽Lambda忒难了,这啥东西都是,他们两个人没有例外都用同一句话怼回我
——这有啥难的。
今天下JDK的时候猛然发现,其实Java已经出Java14了。Java8都出了老久了,但是自己还没摸清楚这次更新最重要的特性,实在是惭愧不已。
于是今天来摸一摸Lambda,StreamAPI.
在各种百度、Google、翻资料、看视频和看书之后,大致上明白了是什么意思了。
虽然根本就没弄清楚没吃透,但是现在我已经可以觉得。。。。。
——诶,Lambda牛逼
——真香!
函数式接口
Java8给出了新东西,函数式接口。具体内容我其实没整明白,但是我感觉其中最靓的地方有两点:
- 抽象方法只能有一个。
- default方法体。
第一点没啥可说的,函数式接口特性。第二点就爽到不行。
之前跟别人一起写东西,假如你要是随随便便把接口给改了,比如你想给接口加个新方法了,那基本上就是约等于找骂,因为别人用了你的接口整出来的类都得适配你这个修改,你一改大家都要改。
这个就很可怕了,久而久之你写的东西就没人用了,怕你瞎改接口。
然后你就会被别人孤立,别人会冷落你。
然后你会陷入人生低谷,久而久之心里会出问题,进入心理亚健康的状态,甚至还有危及生命的风险。
我觉得这也是Java程序员风险高的原因(误
而有了这个default方法体,interface定义的方法直接可以整出默认的实现方式,你加了这个方法之后,队友爱改不改,真是关爱生命、挽救生命的壮举,我必须点赞?
Lambda
Lambda基于函数式编程的思路展开,以前要是有个接口,如果你想实现它,然后创建一个实例,这咋弄呢?
public class TestJava {
public interface DemoInterface {
public void test();
}
public static void main(String[] args) {
DemoInterface demoInterface = new DemoInterface(){
@Override
public void test() {
System.out.println("test!");
}
};
demoInterface.test();
}
}
这不难理解,定义好的接口直接实现一下,调用实例的对象就成了。
但是拼一拼,这个DemoInterface
是一个函数式接口,它肯定只有一个方法需要复写(也就是test
方法)。我写这么多代码去实现这个方法,这多累啊,毕竟我还是个孩子。
然后Lambda就能解决这个问题了。写个啥玩意儿啊,DemoInterface
是可以推断出来的,方法名因为只有那一个,所以完全能省去,其实大部分代码都能省啊。
那就赶紧把Lambda安排上,看看效果:
public class TestJava {
public static void main(String[] args) {
DemoInterface demoInterface = () -> {
System.out.println("test!");
};
demoInterface.test();
}
public interface DemoInterface {
public void test();
}
}
成了,现在我把匿名类的定义和方法的定义大部分都给省了,如果test
方法有参数比如String str
,其实在main
那里用的时候,这个参数类型也能省去,画风大概是这样的:
public class TestJava {
public static void main(String[] args) {
DemoInterface demoInterface = (str) -> {
System.out.println(str);
};
demoInterface.test("test!!!");
}
public interface DemoInterface {
public void test(String str);
}
}
其实还能继续省,因为体里只有一行sout,这种一行代码的话直接就能把花括号给它去了。
main
方法里头就变成这样了:
DemoInterface demoInterface = (str) -> System.out.println(str);
demoInterface.test("test!!!");
最后我们实现这个方法并调用加起来,实现一共一行,调用一共一行,代码变少了,可阅读性更高了,这就很帅。
StreamAPI
Java8还有一个厉害的地方就是StreamAPI.
举个例子,比如以前要给List<Integer> nums = Arrays.asList(1,4,2,6,7,5);
每个数字都加个1,这咋整?
其实一个for循环就能解决问题了。
List<Integer> nums = Arrays.asList(1,4,2,6,7,5);
List<Integer> newNums = new ArrayList<>();
for(int n:nums)
newNums.add(n+1);
nums = newNums;
但是用上StreamAPI之后,这就忒简单了,画风是这个样的:
List<Integer> nums = Arrays.asList(1,4,2,6,7,5);
nums = nums.stream()
.map(n->n=n+1) //这里别写n++!!!
.collect(Collectors.toList());
瞬间就感觉写代码跟起飞的似的。
现在就可以把List
啊数组啊这种转换为Stream,进行操作,最后用collect
可以把Stream
重新收集为指定的类型。
当然,骚操作不局限于此,还有很多操作,都很骚。
并行(xíng)
上面那种处理是串行Stream,就是挨着顺序处理。
也有并行处理,并行处理处理数组类的流比较快。
有状态和无状态
要是非得说这俩词是什么意思,那么肯定是解释不清。但是意思理解了就通了。
我自己觉得主要区别体现在几个应用实例上,比如操作sort
是有状态操作,对数字排序,要是并行时,因为它弄的是类似“一组一组来,最后和一起”的走法,那这样弄毛病就很大,数据量稍微大一点,排序结果就是乱的。
所以排序是一种有状态的操作,这种操作的执行要看别人,但是有些“只靠自己”的操作,比如map
,这种就是逐一修改型的操作,是中无状态操作。
这样理解肯定有问题,但是我觉得没啥大问题。
咋调试
其实我看到的最大的问题就是这样调试不方便。
假如是for写出来,用IDEA的debugger可以断点看到各个步骤,但是这样用StreamAPI一弄,这咋看哪里出问题了?
或者说,我看不清楚各个步骤的问题。
后来终于发现,IDEA有Stream Trace,Stream操作可以一步步看操作过程。
DEBUGGER用的时候在stream第一行断个点就行了,忒爽了。数字咋变的,数据位置咋变的,都标的清清楚楚的。
爱了爱了!
总结
当然,用法根本不局限于此,具体我还要多学习学习。
总之,Java8的这些特性是真的香。