java8

jdk8以前,尤爲是在寫GUI程序的事件監聽的時候,各類的匿名內部類,大把大把拖沓的代碼,程序毫無美感可言!既然java中一切皆爲對象,那 麼,就相似於某些動態語言同樣,函數也能夠當成是對象啊!代碼塊也能夠當成是對象啊!隨着函數式編程的概念愈來愈深刻人心,java中 CODE=OBJECT的這一天終於到來了!若是你認爲lambda表達式僅僅是爲了從語法上簡化匿名內部類,那就過小看jdk8的lambda了! html

下面咱們就來看下lambda表達式是如何亮瞎你的眼的! java

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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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.         12return          
  11.       LineNumberTable:  
  12.         line 30  
  13.         line 46  
  14.         line 512  
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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

很顯然,編譯器會檢查變量的引用類型裏面是否真的是一個函數式接口。那麼如何讓這段代碼經過編譯呢?
只須要加一個強制類型轉換就能夠了: express

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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

若是上面的內容看上去平淡無奇的話,真正的大殺器出現了:方法引用
咱們有一個這樣的類: json

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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幫咱們作了這件事: api

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  1. people  
  2.       .stream()  
  3.       .filter(it -> it.getAge() >= 21) ;  
過濾age>12的元素,並輸出:
[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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
相關文章
相關標籤/搜索