欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。
上个月(2018年2月)看过一份调研报告,对于Java版本而言,生产环境中用的最多的是JDk6和JDK7,虽然JDK8在自2014年3月发布至今使用占比仍然很小,想想月底JDK10都要出来了呀。JDk8引入了很多新的特性,比如接口默认值、方法引用、Lambda表达式、函数式接口、Optional、Stream等等,这些在其他语言中并不少见的玩意儿,现今在Java中却还很少使用,很多时候我们会对新事物(其实已经不新鲜)抱着一种比较保守的态度:反正我司还没用到,学了也无用武之地;让别人先去踩踩坑、我来大树底下好乘凉;又学新东西?我JDK7都没学完,又TMD要学JDk8、9、10?累觉不爱啊,笔者也一直保持着这种心态,但是一颗好奇的心促使我小小的往前迈了那么一小步……
平常撸代码的时候经常也会涉及一些多线程的东西,比如使用Future和Callable来搞一些事情,代码举例如下:
对于这段代码在搞什么事情这里就不多说了,值得注意的是IDEA里给“new Callable()”这段代码“特殊”照顾了一下(注意IDE的language level要设置成JDK8及以上的),想必是要告诉我一些隐晦的事情,本着一颗少男的好奇心我默默的浏览了下提示信息,详细如下图:
内心的躁动让我小小点击了“Replace with lambda”一下?代码迅速地做了切换:
What an AMAZING thing! 代码立马简洁了许多,整个Callable接口的实现只保留了关键的“()->“lambda demo””。这个不止在使用Callable的时候发生,常用的Runnable、Comparator中都会如此,JDK8到底对它们做了什么改变?! 我们不妨举一个DEMO从最初的夙愿来一步步拨开这一层层面纱~~
DEMO需求:对一个数字型的字符串列表做排序,这个列表很简单,具体如下:
List list = Arrays.asList("2", "3", "1", "4");
这个需求很简单,实现起来不需要1分钟:
Comparator comparator = new Comparator() {
@Override
public int compare(String o1, String o2) {
return Integer.valueOf(o1) - Integer.valueOf(o2);
}
};
list.sort(comparator);
注意本文中所使用的JDK版本为8,所以你看到List的sort()方法的时候不要感到太意外,这是List接口中用默认方法实现的一个新方法:default void sort(Comparator super E> c)。对于上面的实现我们还可以很傲娇的改用一下匿名类的方式:
list.sort(new Comparator() {
@Override
public int compare(String o1, String o2) {
return Integer.valueOf(o1) - Integer.valueOf(o2);
}
});
如果是JDK7,那么用一下Collections.sort()方法,然后差不多实现到这里就结束了,然后JDK8才刚刚开始,我们可以通过进一步的把这个方法改为Lambda表达式的形式实现:
list.sort((String o1, String o2) -> Integer.valueOf(o1) - Integer.valueOf(o2));
好了,一下子就变成一行代码了,只不过看上去比原先的有那么一丢丢的晦涩。正如上面所说的Callable也好、Runnable也好,和Comparator这种都属于函数式接口,如果你打开源码可以发现这三个接口都有一个相同的注解——@FuncationalInterface。如果你所使用的方法中包含有函数式接口,那么你就可以使用Lambda表达式。函数式接口是一种有且只有一个抽象方法的接口,如果标注为@FunctionalInterface的接口没有抽象方法(空接口也就是标记接口,或者接口中的方法都是默认方法)或者拥有超过一个抽象方法的话都会报错。
类似于( //会报错:No target method found.):
@FunctionalInterface
public interface FunctionError{
}
或者这样(//会报错:Multiple non-overriding abstract methods found in interface FunctionError):
@FunctionalInterface
public interface FunctionError{
public String method1(String o1, String o2);
public String method2(Integer o1, Boolean o2);
}
都是无效的,可以将上面的其中一个方法改为默认方法:
@FunctionalInterface
public interface FunctionCorrect{
public String method1(String o1, String o2);
default public String method2(Integer o1, Boolean o2){
return "why not rabbitmq or kafka?";
};
}
这样就没有问题。
这里我们先来小结一下——Lambda表达式一个有三个部分:
(参数列表)-> 表达式
或者:
(参数列表)-> {语句}
Java还可以推断出Lambda表达式中的参数类型,上面的代码还可以进一步优化去掉参数类型的声明:
list.sort((o1, o2) -> Integer.valueOf(o1) - Integer.valueOf(o2));
对于上面的代码中Integer本身就具有“可比性”(实现了Comparable)接口,上面的代码可以改写成:
list.sort((o1, o2) -> Integer.valueOf(o1).compareTo(Integer.valueOf(o2)));
不过这样的代码易读性好像也不是很高,我们这里做进一步的改进,这里引入了一个新的概念——方法引用。方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递他们,在某些情况下,他们更加的易读。上面的代码可以进一步的改写成:
list.sort(Comparator.comparing(Integer::valueOf));
这样的代码可以看出我们对于String列表的排序规则时其int类型的值,而不用再关注有点晦涩的Lambda语句。如果有一天宝宝不开心了,不按照其转换的int值排序,而是按照其hashCode排序怎么办呢,很简单:
list.sort(Comparator.comparing(String::hashCode));
如果宝宝又不开了,原本是升序排序的,现在要按降序排序的怎么办呢?改写Lambda表达式,比如这样:
list.sort((o1, o2) -> Integer.valueOf(o2).compareTo(Integer.valueOf(o1)));
不如这样:
list.sort(Comparator.comparing(Integer::valueOf).reversed());
//or
list.sort(Comparator.comparing(String::hashCode).reversed());
是不是通俗易懂又方便?方法引用的基本思想是:如果一个Lambda代表的只是直接调用这个方法,那最好还是用名称来调用它,而不是去描述如何调用它。事实上,方法引用就是让你根据已有的方法实现来创建Lambda表达式,只不过显示地指明方法的名称,代码可读性会高一点。当你需要使用方法引用时,将目标引用放在分隔符::前,而方法的名称放在后面。例如:
(String s) -> System.out.println(s) 可以等效为 System.out:println
要不你try一下下面的代码看看效果是不是一样的?
public class FunctionDemo {
@FunctionalInterface
public interface FunctionQuote{
public void print(String arg);
}
public static void process(FunctionQuote functionQuote){
String str = "http://blog.csdn.net/u013256816";
functionQuote.print(str);
}
public static void main(String[] args) {
process((String s) -> System.out.println(s));
process(System.out::println);
}
}
欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。