提升你的代码——Lambda!

提升你的代码——Lambda!

前言

上个月(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<String> list = Arrays.asList("2", "3", "1", "4");

这个需求很简单,实现起来不需要1分钟:

Comparator<String> comparator = new Comparator<String>() {
@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<String>() {
@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表达式一个有三个部分:

  1. 参数列表。对于上面的Comparable接口而言其参数列表就是“public int compare(String o1, String o2)”中的“(String o1, String o2)”。对于Callable/Runnable来说,其call/run方法没有参数,所以这里可以表示为一个简单的括号()。
  2. 箭头符号:->。
  3. Lambda表达式主体。也就是“Integer.valueOf(o1) - Integer.valueOf(o2);”。对于本文中第一个示例而言,其Lambda主题即为:“”lambda demo.””。Lambda表达式主体的返回值类型 = 函数式接口中方法的返回值类型相同。

可以看到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实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客(ID: hiddenkafka)。
本文作者: 朱小厮

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×