java9的一再推遲發佈,彷佛讓咱們恍然想起離發佈java8已通過去了三年之久,java8應該算的上java語言在歷代版本中變化最大的一個版本了,最大的新特性應該算得上是增長了lambda表達式,藉助lambda表達式,咱們能夠編寫出性能更好,可讀性更強的代碼,更重要的,它給咱們帶來了一種編程思想的改革,做爲一個活躍了20多年的編程語言,java可以作到與時俱進,擁抱新變化,實屬不易,雖然如今不少公司包括我所在的公司還沒有把jdk升級到最新的版本,但關注並學習每個版本帶來的新變化,是每一個java程序員都該作的事,由於正是這些變化表明了這門語言將來的發展方向.能夠預想,幾年之後lambda表達式必將在整個java開發領域完成普及和應用,所以如今,對咱們來講,是時候把lambda表達式學起來了,廢話就說這麼多,下面就讓咱們瞭解一下lambda的強大.java
拿一個簡單的例子來講,若是咱們須要遍歷一個List集合,須要怎麼作,通常是下邊這樣:程序員
List<String> lists=Arrays.asList("a","b","c","d"); for (String s:lists){ System.out.println(s); }
java8給我提供了Collection.forEach()方法,因而咱們能夠這樣編程:編程
List<String> lists=Arrays.asList("a","b","c","d");
lists.forEach(i-> System.out.println(i));
這是一個最簡單的例子,可是咱們能看出來一點,以前遍歷是寫在外部的,即客戶端代碼,而使用了lambda表達式將迭代的操做放到了forEach()方法中,即封裝到了java類庫中,這樣對外界是沒有任何侵入性的,如今不用糾結於這麼一行代碼究竟是怎麼實現的,是什麼意思,下面會慢慢剖析. app
在數學計算的角度,lambda表達式指的是一種函數,但在java中如今還不能編寫一個獨立的函數,畢竟java不是函數式編程語言,在java中,lambda能夠看作一種匿名方法,一種格式很是簡潔的匿名方法,能夠省略修飾符,返回類型等.編程語言
lambda表達式能夠分解成三部分來看: (1)參數列表 (2)-> (3)lambda體,如上面例子中的函數式編程
i -> System.out.println(i))函數
左邊是參數列表,lambda能夠接收任意多個參數,當參數數量大於1時,須要將參數括起來,如 性能
(i,j)->System.out.println(i+j))學習
若是不須要指定參數,須要使用()來表示無參,如測試
()->System.out.println("hello lambda")
能夠看到咱們並無聲明參數的數據類型,這是由於不少狀況下,編譯器能從上下文中推導出數據的參數類型,固然咱們能夠顯示的指定類型
(int i,int j)->System.out.println(i+j))
函數箭頭的右側是lambda體,上面的例子中只有一行代碼,當有多行代碼時,須要使用{}括起來,如
(i,j) ->{System.out.println(i);System.out.println("----"); }
若是lambda體中的表達式有返回值,須要使用return來返回,也能夠後面跟一個參數來停止lambda體,
(i,j)->return i+j 或 (i,j)->i+j;
理解函數式接口,是學習lambda表達式的關鍵,函數式接口的定義其實比較簡單,對於任意一個接口(interface),若是他只包含一個(抽象)方法,那麼這個接口就能夠稱之爲函數式接口,這種接口被@FunctionalInterface註解標示,如今咱們來回憶一下在java8之前,咱們常常碰到的函數式接口.
public interface Runnable { void run(); }
public interface Callable<V> { V call() throws Exception; }
public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }
如今咱們以第三個方法Comparator來舉例,以往咱們使用這個類來實現自定義功能通常是這樣的,這裏假設咱們須要以Person的age來對Person對象進行排列,通常這樣實現:
1 Person[] persons=new Person[]{new Person("a",7),new Person("b",9),new Person("c",5)}; 3 Arrays.sort(persons, new Comparator<Person>() { 4 public int compare(Person o1, Person o2) { 5 return Integer.compare(o1.getScore(),o2.getScore()); 6 } 7 }); 9 System.out.println(Arrays.toString(persons));
Person類這裏再也不給出定義,Person有兩個屬性name和age.運行程序,在打印臺咱們能夠看到輸出順序是 c,a,b
咱們已經知道Comparator是一個函數式接口,咱們可使用lambda表達式來獲得一個實例,如今咱們來觀察sort方法的第二個參數,它接受一個Comparator對象用於定義比較的規則,new Comparator<Person>表示new一個Comparatotr對象,這不是廢話嗎,sort方法已經定義了第二個參數必須爲Comparator對象,所以這段代碼是能夠省略的,由於聰明的編譯器能夠從上下文中推測出來,那讓咱們來看一下,還有什麼東西是編譯器可以推測出來的,咱們稍加思索便會發現,public int compare,返回類型,o1,o2的數據類型,都是能夠推測出來的,由於只有這一個方法嘛,那麼使用lambda改造一下的模樣是這樣的.
Person[] persons=new Person[]{new Person("a",7),new Person("b",9),new Person("c",5)};
Arrays.sort(persons, ( o1, o2) -> Integer.compare(o1.getScore(),o2.getScore()) );
System.out.println(Arrays.toString(persons));
一個匿名內部類對象就這樣被咱們用lambda表達式改形成上面這樣子,咱們能夠這麼理解,上面紅色加粗的代碼,就至關於建立了一個Comparator對象並重寫了compare方法內容.這時候,你該問了,這不就是一個匿名內部類的語法糖嗎?事實上,從語法上看,的確很像一個語法糖,但兩者之間存在不少顯著的差別.這裏暫時不作深刻討論.
在java8中引入了一個新的包java.util.function,這個包中定義了不少函數式接口用於支持lambda表達式,下面簡單介紹幾個常見接口.
Function接口,有一個參數,而且返回一個結果,觀察該接口,咱們發現Function能接受一個T類型的參數並返回R類型,這裏使用泛型有更好的擴展性.
@FunctionalInterface public interface Function<T, R> { R apply(T t); }
下面咱們使用Function接口來寫一個小例子,以下:
public static int changeTheValue(int value ,Function<Integer,Integer> function){ return function.apply(value); } @Test public void testChangeTheValue(){ int value=10; int result1=changeTheValue(value,i->i+5); System.out.println("result1:"+result1); int result2=changeTheValue(value,(i)->i*30); System.out.println("result2"+result2); }
上述代碼中,咱們定義了一個方法changeTheValue,接收兩個參數,第一個是須要修改的value,第二個是一個function對象,在下面的測試方法中,咱們根據本身的想法靈活的改變第二個參數,能夠獲得對象的結果.運行測試,能夠獲得result1:15 result2:300.
除了Function接口以外,類庫還提供了一個Consumer接口,他和Function接口惟一的區別就是,方法沒有返回值,還記得最上面咱們遍歷集合的時候使用的list.forEach方法嗎,它接受的就是一個Consumer實例.
類庫給咱們預約義的函數式接口,固然不止上面提的這兩個,還有一個Predicate,他返回一個boolean類型的值,用來判斷某項條件是否知足,常常用來進行篩濾操做,其餘接口這裏也再也不討論.
咱們初步認識了lambda的簡單語法結構: 參數列表->lambda體
lambda的函數式接口(目標類型),java8引進了用於支持lambda表達式的java.util.Function接口
lambda表達式的應用場景之一是替換以前普遍使用的匿名內部類的常規語法.
<精通lambda表達式:java多核編程>