关键词搜索

源码搜索 ×
×

提升你的代码——Lambda!

发布2018-03-11浏览2505次

详情内容


欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。

欢迎跳转到本文的原文链接:https://honeypps.com/java/java-lambda-quick-start/


前言

上个月(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("https://cdn.jxasp.com:9143/image/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);
    
      2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意本文中所使用的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);
        }
    });
    
      2
    • 3
    • 4
    • 5
    • 6

    如果是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{
      
      }
      
        2
      • 3
      • 4

      或者这样(//会报错: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);
      }
      
        2
      • 3
      • 4
      • 5

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

      @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?";
          };
      }
      
        2
      • 3
      • 4
      • 5
      • 6
      • 7

      这样就没有问题。

      这里我们先来小结一下——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());
                    
                      2
                    • 3

                    是不是通俗易懂又方便?方法引用的基本思想是:如果一个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);
                          }
                      }
                      
                        2
                      • 3
                      • 4
                      • 5
                      • 6
                      • 7
                      • 8
                      • 9
                      • 10
                      • 11
                      • 12
                      • 13
                      • 14
                      • 15
                      • 16

                      欢迎跳转到本文的原文链接:https://honeypps.com/java/java-lambda-quick-start/


                      欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。


                      相关技术文章

                      点击QQ咨询
                      开通会员
                      返回顶部
                      ×
                      微信扫码支付
                      微信扫码支付
                      确定支付下载
                      请使用微信描二维码支付
                      ×

                      提示信息

                      ×

                      选择支付方式

                      • 微信支付
                      • 支付宝付款
                      确定支付下载