提升你的代码——Lambda!

1,244 阅读6分钟
原文链接: mp.weixin.qq.com

前言

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

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):

@FunctionalInterfacepublic interface FunctionError{    public String method1(String o1, String o2);    public String method2(Integer o1, Boolean o2);}

都是无效的,可以将上面的其中一个方法改为默认方法:

@FunctionalInterfacepublic interface FunctionCorrect{    public String method1(String o1, String o2);    default public String method2(Integer o1, Boolean o2){        return "why not rabbitmq or kafka?";    };}

这样就没有问题。

这里我们先来小结一下——Lambda表达式一个有三个部分:

  • 参数列表。对于上面的Comparable接口而言其参数列表就是“public int compare(String o1, String o2)”中的“(String o1, String o2)”。对于Callable/Runnable来说,其call/run方法没有参数,所以这里可以表示为一个简单的括号()。

  • 箭头符号:->。

  • 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());//orlist.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);    }}

如果读完觉得有收获的话,欢迎点赞、关注、加公众号【匠心零度】,查阅更多精彩历史!!!