zoukankan      html  css  js  c++  java
  • jdk8新特性-亮瞎眼的lambda表达式

    jdk8之前,尤其是在写GUI程序的事件监听的时候,各种的匿名内部类,大把大把拖沓的代码,程序毫无美感可言!既然java中一切皆为对象,那么,就类似于某些动态语言一样,函数也可以当成是对象啊!代码块也可以当成是对象啊!随着函数式编程的概念越来越深入人心,java中CODE=OBJECT的这一天终于到来了!如果你认为lambda表达式仅仅是为了从语法上简化匿名内部类,那就太小看jdk8的lambda了!

    下面我们就来看下lambda表达式是如何亮瞎你的眼的!

    lambda的定义
    Funda-men-tally, a lambda expression is just a shorter way of writing an implementation of a method for later execution. 
    (1)lambda是方法的实现
    (2)lambda是延迟执行的

    首先看一个用匿名内部类的例子:

    [java] view plain copy
     
    1. public class Test1{  
    2.         public static void main(String args[]){  
    3.                 Runnable r = new Runnable(){  
    4.                         public void run(){  
    5.                                 System.out.println("hello,lambda!");  
    6.                         }  
    7.                 };  
    8.                 r.run();  
    9.         }  
    10. }  

    要换成lambda是什么样的呢?

    [java] view plain copy
     
    1. public class Test2{  
    2.         public static void main(String args[]){  
    3.                 Runnable r = ()->System.out.println("hello,lambda");  
    4.                 r.run();  
    5.         }  
    6. }  

    原先要5行代码,现在换成了仅仅1行!
    这他妈的得省多少代码啊!
    有木有很兴奋啊!
    下面还有更刺激的!

    lambda是如何做到的呢?看一下反编译之后的字节码:

    [java] view plain copy
     
    1. public static void main(java.lang.String[]);  
    2.     descriptor: ([Ljava/lang/String;)V  
    3.     flags: ACC_PUBLIC, ACC_STATIC  
    4.     Code:  
    5.       stack=1, locals=2, args_size=1  
    6.          0: invokedynamic #2,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;  
    7.          5: astore_1        
    8.          6: aload_1         
    9.          7: invokeinterface #3,  1            // InterfaceMethod java/lang/Runnable.run:()V  
    10.         12: return          
    11.       LineNumberTable:  
    12.         line 3: 0  
    13.         line 4: 6  
    14.         line 5: 12  
    15. }  

    注意:上面有一个叫做invokedynamic的指令。invokedynamic是从jdk7开始引入的,jdk8开始落地。
    可以看出来lambda并不是语法糖,它不是像匿名内部类那样生成那种带有$的匿名类。
    简单的说,这里只是定义了一个方法调用点,具体调用那个方法要到运行时才能决定,这就是前面所说的:延迟执行。
    具体的细节请google:invokedynamic。

    为了配合lambda,jdk8引入了一个新的定义叫做:函数式接口(Functional interfaces)
    函数式接口:
    it is an interface that requires exactly one method to be implemented in order to satisfy the requirements of the interface. 
    (1)是一个接口
    (2)只有一个待实现的方法
    因为jdk8开始,接口可以有default方法,所以,函数式接口也是可以有default方法的,但是,只能有一个未实现的方法。

    与此对应,新引入了一个注解: @FunctionalInterface
    这个注解只是起文档的作用,说明这个接口是函数式接口,编译器并不会使用这个注解来决定一个接口是不是函数式接口。
    不管加不加@FunctionalInterface这个注解,下面的接口都是函数式接口:
    interface Something {
      public String doit(Integer i);
    }

    lambda的语法
    A lambda in Java essentially consists of three parts: a parenthesized set of parameters, an arrow, and then a body, 
    which can either be a single expression or a block of Java code.
    lambda包含3个部分:
    (1)括弧包起来的参数
    (2)一个箭头
    (3)方法体,可以是单个语句,也可以是语句块
    参数可以写类型,也可以不写,jvm很智能的,它能自己推算出来

    [java] view plain copy
     
    1. public class Test3{  
    2.         public static void main(String... args) {  
    3.             Comparator<String> c = (String lhs, String rhs) -> lhs.compareTo(rhs);  
    4.             int result = c.compare("Hello", "World");  
    5.                 System.out.println(result);  
    6.         }  
    7. }  

    方法可以有返回,也可以无返回,如果有多个语句,还要返回值,需要加上return

    [java] view plain copy
     
    1. public class Test4{  
    2.         public static void main(String... args) {  
    3.             Comparator<String> c =(lhs, rhs) ->{  
    4.                       System.out.println("I am comparing " +lhs + " to " + rhs);  
    5.                           return lhs.compareTo(rhs);  
    6.                 };  
    7.                 int result = c.compare("Hello", "World");  
    8.                 System.out.println(result);  
    9.         }   
    10. }  



    一个很有意思的事情:
    之前我们说Object是一切类的父类,然而在加入了lambda以后,这种大一统的局面将不复存在:

    [java] view plain copy
     
    1. public class Test5{  
    2.         public static void main(String args[]){  
    3.                 Object r = ()->System.out.println("hello,lambda");  
    4.         }  
    5.   
    6. }  

    编译报错:
    Test5.java:3: error: incompatible types: Object is not a functional interface
                    Object r = ()->System.out.println("hello,lambda");
                               ^
    1 error

    很显然,编译器会检查变量的引用类型里面是否真的是一个函数式接口。那么如何让这段代码通过编译呢?
    只需要加一个强制类型转换就可以了:

    [java] view plain copy
     
    1. public class Test6{  
    2.         public static void main(String args[]){  
    3.                 Object r = (Runnable)()->System.out.println("hello,lambda");  
    4.         }  
    5.   
    6. }  

    lambda的词法作用域

    我们知道,在匿名内部类中:

    [java] view plain copy
     
    1. class Hello {  
    2.   public Runnable r = new Runnable() {  
    3.       public void run() {  
    4.         System.out.println(this);  
    5.         System.out.println(toString());  
    6.       }  
    7.     };  
    8.   
    9.   public String toString() {  
    10.     return "Hello's custom toString()";  
    11.   }  
    12. }  
    13.   
    14. public class InnerClassExamples {  
    15.   public static void main(String... args) {  
    16.     Hello h = new Hello();  
    17.     h.r.run();  
    18.   }  
    19. }   

    System.out.println(this);这里的this指的是匿名类,而非Hello类。
    想要引用Hello类需要Hello.this这样:

    [java] view plain copy
     
    1. class Hello {  
    2.   public Runnable r = new Runnable() {  
    3.       public void run() {  
    4.         System.out.println(Hello.this);  
    5.         System.out.println(Hello.this.toString());  
    6.       }  
    7.     };  
    8. }  

    这种做法非常的反人类反直觉!看上去很恶心!
    下面我们就来看一下伟大的lambda是什么样子的:

    [java] view plain copy
     
    1. class Hello{  
    2.         public Runnable r = () -> {  
    3.              System.out.println(this);  
    4.              System.out.println(toString());  
    5.         };  
    6.   
    7.         public String toString() {  
    8.                   return "Hello's custom toString()";  
    9.         }  
    10. }  
    11. public class Test7{  
    12.         public static void main(String args[]){  
    13.                     Hello h = new Hello();  
    14.                     h.r.run();  
    15.         }  
    16. }  

    输出:
    Hello's custom toString()
    Hello's custom toString()
    System.out.println(this);这里的this指的是Hello,而非lambda表达式!
    但是,如果我们想在lambda表达式中返回lambda本身该怎么做呢?

    变量捕获
    匿名内部类只能引用作用域外面的final的变量,在lambda中对这个限制做了削弱,只需要是“等价final”就可以,没必要用final关键字来标识。

    [java] view plain copy
     
    1. public class Test8{  
    2.         public static void main(String args[]){  
    3.                 String message = "Howdy, world!";//不需要是final的  
    4.                 Runnable r = () -> System.out.println(message);//这里也能访问  
    5.                 r.run();  
    6.         }  
    7. }  

    “等效final”的意思是:事实上的final,所以,一旦赋值也是不可以改变的!比如:

    [java] view plain copy
     
    1. public class Test9{  
    2.         public static void main(String args[]){  
    3.                 String message = "Howdy, world!";  
    4.                 Runnable r = () -> System.out.println(message);  
    5.                 r.run();  
    6.                 message = "change it";  
    7.         }  
    8. }  

    Test9.java:4: error: local variables referenced from a lambda expression must be final or effectively final
                    Runnable r = () -> System.out.println(message);
                                                          ^
    1 error

    如果上面的内容看上去平淡无奇的话,真正的大杀器出现了:方法引用
    我们有一个这样的类:

    [java] view plain copy
     
    1. class Person {  
    2.   public String firstName;  
    3.   public String lastName;  
    4.   public int age;  
    5. }  

    现在我们要把多个Person对象进行排序,有时候是按照firstName来排,有时候是按照lastName或者是age来排,使用lambda可以这样来做:

    [java] view plain copy
     
    1. class Person{  
    2.         public String firstName;  
    3.         public String lastName;  
    4.         public int age;  
    5.         public Person(String firstName, String lastName, int age){  
    6.                 this.firstName = firstName;  
    7.                 this.lastName = lastName;  
    8.                 this.age = age;  
    9.         }  
    10.         public String toString(){  
    11.                 return firstName+","+lastName+","+age;  
    12.         }  
    13. }  
    14. public class Test10{  
    15.         public static void main(String args[]){  
    16.                 Person people[] = new Person[]{  
    17.                         new Person("Ted", "Neward", 41),  
    18.                         new Person("Charlotte", "Neward", 41),  
    19.                         new Person("Michael", "Neward", 19),  
    20.                         new Person("Matthew", "Neward", 13)  
    21.                 };  
    22.                 //sort by firstName  
    23.                 Arrays.sort(people, (lhs,rhs)->lhs.firstName.compareTo(rhs.firstName));  
    24.                 for(Person p : people){  
    25.                         System.out.println(p);  
    26.                 }  
    27.         }  
    28. }  

    我们可以把Comparator抽取出来,变成是Person对象的成员变量:

    [java] view plain copy
     
    1. class Person{  
    2.         public String firstName;  
    3.         public String lastName;  
    4.         public int age;  
    5.         public Person(String firstName, String lastName, int age){  
    6.                 this.firstName = firstName;  
    7.                 this.lastName = lastName;  
    8.                 this.age = age;  
    9.         }  
    10.         public String toString(){  
    11.                 return firstName+","+lastName+","+age;  
    12.         }  
    13.         public final static Comparator<Person> compareFirstName =  
    14.             (lhs, rhs) -> lhs.firstName.compareTo(rhs.firstName);  
    15.   
    16.         public final static Comparator<Person> compareLastName =  
    17.             (lhs, rhs) -> lhs.lastName.compareTo(rhs.lastName);  
    18.   
    19.         public final static Comparator<Person> compareAge =  
    20.             (lhs, rhs) -> lhs.age - rhs.age;  
    21. }  
    22. public class Test11{  
    23.         public static void main(String args[]){  
    24.                 Person people[] = new Person[]{  
    25.                         new Person("Ted", "Neward", 41),  
    26.                         new Person("Charlotte", "Neward", 41),  
    27.                         new Person("Michael", "Neward", 19),  
    28.                         new Person("Matthew", "Neward", 13)  
    29.                 };  
    30.                 Arrays.sort(people, Person.compareFirstName);//这里直接引用lambda  
    31.                 for(Person p : people){  
    32.                         System.out.println(p);  
    33.                 }  
    34.         }  
    35. }  

    能起到同样的作用,但是语法看上去很奇怪,因为之前我们都是创建一个满足Comparator签名的方法,然后直接调用,而非定义一个变量,
    然后引用这个变量!所以,还有这么一种调用方法:

    [java] view plain copy
     
    1. class Person{  
    2.         public String firstName;  
    3.         public String lastName;  
    4.         public int age;  
    5.         public Person(String firstName, String lastName, int age){  
    6.                 this.firstName = firstName;  
    7.                 this.lastName = lastName;  
    8.                 this.age = age;  
    9.         }  
    10.         public String toString(){  
    11.                 return firstName+","+lastName+","+age;  
    12.         }  
    13.         public static int compareFirstName(Person lhs, Person rhs){  
    14.                 return lhs.firstName.compareTo(rhs.firstName);  
    15.         }   
    16.         public static int compareLastName(Person lhs, Person rhs){  
    17.              return lhs.lastName.compareTo(rhs.lastName);  
    18.         }  
    19. }  
    20. public class Test12{  
    21.         public static void main(String args[]){  
    22.                 Person people[] = new Person[]{  
    23.                         new Person("Ted", "Neward", 41),  
    24.                         new Person("Charlotte", "Neward", 41),  
    25.                         new Person("Michael", "Neward", 19),  
    26.                         new Person("Matthew", "Neward", 13)  
    27.                 };  
    28.                 Arrays.sort(people, Person::compareFirstName);  
    29.                 for(Person p : people){  
    30.                         System.out.println(p);  
    31.                 }  
    32.         }  
    33. }  

    看Person::compareFirstName这种调用方式,
    如果是static方法使用:类名::方法名
    如果是instance方法:instance::方法名

    但是,上面的代码还是不是很美观,因为Person只是一个数据对象,它不应该的对外提供compareFirstName或者是compareLastName这样的方法,
    我们需要的仅仅是根据某个字段排序而已!很幸运的是jdk的api帮我们做了这件事:

    [java] view plain copy
     
    1. import java.util.*;  
    2. class Person{  
    3.         public String firstName;  
    4.         public String lastName;  
    5.         public int age;  
    6.         public Person(String firstName, String lastName, int age){  
    7.                 this.firstName = firstName;  
    8.                 this.lastName = lastName;  
    9.                 this.age = age;  
    10.         }  
    11.         public String getFirstName(){  
    12.                 return this.firstName;  
    13.         }  
    14.         public String getLastName(){  
    15.                 return this.lastName;  
    16.         }  
    17.         public String toString(){  
    18.                 return firstName+","+lastName+","+age;  
    19.         }  
    20. }  
    21. public class Test13{  
    22.         public static void main(String args[]){  
    23.                 Person people[] = new Person[]{  
    24.                         new Person("Ted", "Neward", 41),  
    25.                         new Person("Charlotte", "Neward", 41),  
    26.                         new Person("Michael", "Neward", 19),  
    27.                         new Person("Matthew", "Neward", 13)  
    28.                 };  
    29.                 Arrays.sort(people, Comparator.comparing(Person::getFirstName));  
    30.                 for(Person p : people){  
    31.                         System.out.println(p);  
    32.                 }  
    33.         }  
    34. }  

    Arrays.sort(people, Comparator.comparing(Person::getFirstName));这里调用了Comparator.comparing方法,
    但是注意这里的Person::getFirstName,很显然getFirstName()并不是static的,这是jdk做了封装的缘故!
    这样做就非常完美了!

    假如我们的排序算法改为:先按照lastName,然后按照age排序呢?

    [java] view plain copy
     
    1. public class Test15{  
    2.         public static void main(String args[]){  
    3.                 Person people[] = new Person[]{  
    4.                         new Person("Ted", "Neward", 10),  
    5.                         new Person("Charlotte", "Neward", 41),  
    6.                         new Person("Michael", "Naward", 19),  
    7.                         new Person("Matthew", "Nmward", 13)  
    8.                 };  
    9.                 Collections.sort(Arrays.asList(people), (lhs, rhs)->{  
    10.                         if(lhs.getLastName().equals(rhs.getLastName())){  
    11.                                 return lhs.getAge()-rhs.getAge();  
    12.                         }else{  
    13.                                 return lhs.getLastName().compareTo(rhs.getLastName());  
    14.                         }  
    15.                 });  
    16.                 for(Person p : people){  
    17.                         System.out.println(p);  
    18.                 }  
    19.         }  
    20. }  

    很显然,应该还有更好的实现方式:

    [java] view plain copy
     
    1. public class Test16{  
    2.         public static void main(String args[]){  
    3.                 Person people[] = new Person[]{  
    4.                         new Person("Ted", "Neward", 10),  
    5.                         new Person("Charlotte", "Neward", 41),  
    6.                         new Person("Michael", "Naward", 19),  
    7.                         new Person("Matthew", "Nmward", 13)  
    8.                 };  
    9.                 Collections.sort(Arrays.asList(people),Comparator.comparing(Person::getLastName).thenComparing(Person::getAge));  
    10.                 for(Person p : people){  
    11.                         System.out.println(p);  
    12.                 }  
    13.         }  
    14. }  

    Comparator.comparing(Person::getLastName).thenComparing(Person::getAge):简直帅呆了!
    还有更多的诸如:andThen()这样的方法,可以查api。

    虚方法扩展
    因为接口可以有default方法,所以很多类库都重写了,加入了一些default的方法,比如:
    interface Iterator<T> {
      boolean hasNext();
      T next();
      void remove();
      void skip(int i) default {
        for (; i > 0 && hasNext(); i--) next();
      }

    skip(i)就是一个default方法,这样所有的Iterator的子类都具有了一个叫skip的方法!
    但是,大家对default方法的争议还是比较大的,比如:

    [java] view plain copy
     
    1. interface I1 {  
    2.         public default void print(){  
    3.                 System.out.println("I1");  
    4.         }  
    5.         public void hello();  
    6. }  
    7. interface I2{  
    8.         public default void print(){  
    9.                 System.out.println("I2");  
    10.         }  
    11.         public void world();  
    12. }  
    13. class Impl implements I1,I2{  
    14.         public void hello(){  
    15.         }  
    16.         public void world(){  
    17.         }  
    18. }  

    如果在Impl上调用print会怎样呢?这不就是传说中的多继承么?想知道结果的话,自己试一下就可以了,哈哈

    Stream:
    之前的文章已经有介绍,下面只据一些使用的例子:
    过滤age>12的元素:

    [java] view plain copy
     
    1. people  
    2.       .stream()  
    3.       .filter(it -> it.getAge() >= 21) ;  

    过滤age>12的元素,并输出:

    [java] view plain copy
     
    1. people.stream()  
    2.      .filter((it) -> it.getAge() >= 21)  
    3.      .forEach((it) ->   
    4.        System.out.println("Have a beer, " + it.getFirstName()));   

    jdk预定义的Predicate:

    [java] view plain copy
     
    1. Predicate<Person> drinkingAge = (it) -> it.getAge() >= 21;  
    2. Predicate<Person> brown = (it) -> it.getLastName().equals("Brown");  
    3. people.stream()  
    4.       .filter(drinkingAge.and(brown))  
    5.       .forEach((it) ->System.out.println("Have a beer, " + it.getFirstName()));  

    map:

    [java] view plain copy
     
    1. IntStream ages =  
    2.       people.stream()  
    3.             .mapToInt((it) -> it.getAge());   
    4. //sum:  
    5. int sum = people.stream()  
    6.                 .mapToInt(Person::getAge)  
    7.                 .sum();   

    重点说下reduce:

    [java] view plain copy
     
    1. public class Test17{  
    2.         public static void main(String args[]){  
    3.                 List<Integer> values = Arrays.asList(1,2,3,4,5);  
    4.                 int sum = values.stream().reduce(0, (l,r)->l+r);  
    5.                 System.out.println(sum);  
    6.         }  
    7. }  

    reduce(0, (l,r)->l+r)的工作原理是:第一个参数0作为后面lambda表达式的左操作数,然后从stream中取出一个元素作为右操作数,
    二者运算的结果作为下一次运算的左操作数,依次循环。
    最后看一个好玩的例子:

    [java] view plain copy
     
    1. class Person{  
    2.         public String firstName;  
    3.         public String lastName;  
    4.         public int age;  
    5.         public Person(String firstName, String lastName, int age){  
    6.                 this.firstName = firstName;  
    7.                 this.lastName = lastName;  
    8.                 this.age = age;  
    9.         }  
    10.         public String getFirstName(){  
    11.                 return this.firstName;  
    12.         }  
    13.         public String getLastName(){  
    14.                 return this.lastName;  
    15.         }  
    16.         public int getAge(){  
    17.                 return this.age;  
    18.         }  
    19.         public String toString(){  
    20.                 return firstName+","+lastName+","+age;  
    21.         }  
    22.         public String toJson(){  
    23.                 return "{"+  
    24.                                 "firstName:""+firstName+"","+  
    25.                                 "lastName:""+lastName+"","+  
    26.                                 "age:"+age +  
    27.                                 "}";  
    28.         }  
    29. }  
    30. public class Test18{  
    31.         public static void main(String args[]){  
    32.                 Person people[] = new Person[]{  
    33.                         new Person("Ted", "Neward", 10),  
    34.                         new Person("Charlotte", "Neward", 41),  
    35.                         new Person("Michael", "Naward", 19),  
    36.                         new Person("Matthew", "Nmward", 13)  
    37.                 };  
    38.                 String json = Arrays.asList(people).stream().map(Person::toJson).reduce("[",(l,r)->l + (l.equals("[")?"":",") + r)+"]";  
    39.                 System.out.println(json);  
    40.         }  
    41. }  

    输出结果:
    [{firstName:"Ted",lastName:"Neward",age:10},{firstName:"Charlotte",lastName:"Neward",age:41},{firstName:"Michael",lastName:"Naward",age:19},{firstName:"Matthew",lastName:"Nmward",age:13}]
    还可以这样:

    [java] view plain copy
     
    1. public class Test19{  
    2.         public static void main(String args[]){  
    3.                 Person people[] = new Person[]{  
    4.                         new Person("Ted", "Neward", 10),  
    5.                         new Person("Charlotte", "Neward", 41),  
    6.                         new Person("Michael", "Naward", 19),  
    7.                         new Person("Matthew", "Nmward", 13)  
    8.                 };  
    9.                 String joined = Arrays.asList(people).stream().map(Person::toJson).collect(Collectors.joining(", "));  
    10.                 System.out.println("[" + joined + "]");   
    11.         }  
    12. }  
    [java] view plain copy
     
    1. 更进一步:  
    2. public class Test20{  
    3.         public static void main(String args[]){  
    4.                 Person people[] = new Person[]{  
    5.                         new Person("Ted", "Neward", 10),  
    6.                         new Person("Charlotte", "Neward", 41),  
    7.                         new Person("Michael", "Naward", 19),  
    8.                         new Person("Matthew", "Nmward", 13)  
    9.                 };  
    10.                 String json = Arrays.asList(people).stream().map(Person::toJson).collect(Collectors.joining(", ", "[", "]"));  
    11.                 System.out.println(json);  
    12.         }  
    13. }  
    14. 如果只能用一个字来形容,那就是perfect!  

    参考:
    http://www.oracle.com/technetwork/articles/java/architect-lambdas-part1-2080972.html
    http://www.oracle.com/technetwork/articles/java/architect-lambdas-part2-2081439.html

  • 相关阅读:
    HDU.2087 剪花布条
    一个acm过来人的心得
    一个acm过来人的心得
    HDU.2190 悼念512汶川大地震遇难同胞——重建希望小学
    HDOJ.2501 Tiling_easy version
    HDOJ.2501 Tiling_easy version
    HDU
    poj3216 Prime Path(BFS)
    poj1426 Find The Multiple (DFS)
    Rikka with Nickname (简单题)
  • 原文地址:https://www.cnblogs.com/Jeremy2001/p/7683614.html
Copyright © 2011-2022 走看看