背景
java9的一再推遲發(fā)布,似乎讓我們恍然想起離發(fā)布java8已經(jīng)過去了三年之久,java8應(yīng)該算的上java語言在歷代版本中變化最大的一個(gè)版本了,最大的新特性應(yīng)該算得上是增加了lambda表達(dá)式,借助lambda表達(dá)式,我們可以編寫出性能更好,可讀性更強(qiáng)的代碼,更重要的,它給我們帶來了一種編程思想的改革,作為一個(gè)活躍了20多年的編程語言,java能夠做到與時(shí)俱進(jìn),擁抱新變化,實(shí)屬不易,雖然現(xiàn)在很多公司包括我所在的公司尚未把jdk升級(jí)到最新的版本,但關(guān)注并學(xué)習(xí)每一個(gè)版本帶來的新變化,是每個(gè)java程序員都該做的事,因?yàn)檎沁@些變化代表了這門語言未來的發(fā)展方向.可以預(yù)想,幾年以后lambda表達(dá)式必將在整個(gè)java開發(fā)領(lǐng)域完成普及和應(yīng)用,因此現(xiàn)在,對(duì)我們來說,是時(shí)候把lambda表達(dá)式學(xué)起來了,廢話就說這么多,下面就讓我們了解一下lambda的強(qiáng)大.
入門
從外部迭代到內(nèi)部迭代
拿一個(gè)簡單的例子來說,如果我們需要遍歷一個(gè)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));
這是一個(gè)最簡單的例子,但是我們能看出來一點(diǎn),之前遍歷是寫在外部的,即客戶端代碼,而使用了lambda表達(dá)式將迭代的操作放到了forEach()方法中,即封裝到了java類庫中,這樣對(duì)外界是沒有任何侵入性的,現(xiàn)在不用糾結(jié)于這么一行代碼到底是怎么實(shí)現(xiàn)的,是什么意思,下面會(huì)慢慢剖析.
lambda表達(dá)式基礎(chǔ)
定義
在數(shù)學(xué)計(jì)算的角度,lambda表達(dá)式指的是一種函數(shù),但在java中現(xiàn)在還不能編寫一個(gè)獨(dú)立的函數(shù),畢竟java不是函數(shù)式編程語言,在java中,lambda可以看做一種匿名方法,一種格式非常簡潔的匿名方法,可以省略修飾符,返回類型等.
語法
lambda表達(dá)式可以分解成三部分來看: (1)參數(shù)列表 (2)-> (3)lambda體,如上面例子中的
i -> System.out.println(i))
左邊是參數(shù)列表,lambda可以接收任意多個(gè)參數(shù),當(dāng)參數(shù)數(shù)量大于1時(shí),需要將參數(shù)括起來,如
(i,j)->System.out.println(i+j))
如果不需要指定參數(shù),需要使用()來表示無參,如
()->System.out.println("hello lambda")
可以看到我們并沒有聲明參數(shù)的數(shù)據(jù)類型,這是因?yàn)楹芏嗲闆r下,編譯器能從上下文中推導(dǎo)出數(shù)據(jù)的參數(shù)類型,當(dāng)然我們可以顯示的指定類型
(int i,int j)->System.out.println(i+j))
函數(shù)箭頭的右側(cè)是lambda體,上面的例子中只有一行代碼,當(dāng)有多行代碼時(shí),需要使用{}括起來,如
(i,j) ->{System.out.println(i);System.out.println("----"); }
如果lambda體中的表達(dá)式有返回值,需要使用return來返回,也可以后面跟一個(gè)參數(shù)來中止lambda體,
(i,j)->return i+j 或 (i,j)->i+j;
函數(shù)式接口
理解函數(shù)式接口,是學(xué)習(xí)lambda表達(dá)式的關(guān)鍵,函數(shù)式接口的定義其實(shí)比較簡單,對(duì)于任意一個(gè)接口(interface),如果他只包含一個(gè)(抽象)方法,那么這個(gè)接口就可以稱之為函數(shù)式接口,這種接口被@FunctionalInterface注解標(biāo)示,現(xiàn)在我們來回憶一下在java8以前,我們經(jīng)常碰到的函數(shù)式接口.
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); }
現(xiàn)在我們以第三個(gè)方法Comparator來舉例,以往我們使用這個(gè)類來實(shí)現(xiàn)自定義功能一般是這樣的,這里假設(shè)我們需要以Person的age來對(duì)Person對(duì)象進(jìn)行排列,一般這樣實(shí)現(xiàn):
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有兩個(gè)屬性name和age.運(yùn)行程序,在打印臺(tái)我們可以看到輸出順序是 c,a,b
我們已經(jīng)知道Comparator是一個(gè)函數(shù)式接口,我們可以使用lambda表達(dá)式來得到一個(gè)實(shí)例,現(xiàn)在我們來觀察sort方法的第二個(gè)參數(shù),它接受一個(gè)Comparator對(duì)象用于定義比較的規(guī)則,new Comparator<Person>表示new一個(gè)Comparatotr對(duì)象,這不是廢話嗎,sort方法已經(jīng)定義了第二個(gè)參數(shù)必須為Comparator對(duì)象,因此這段代碼是可以省略的,因?yàn)槁斆鞯木幾g器可以從上下文中推測出來,那讓我們來看一下,還有什么東西是編譯器能夠推測出來的,我們稍加思索便會(huì)發(fā)現(xiàn),public int compare,返回類型,o1,o2的數(shù)據(jù)類型,都是可以推測出來的,因?yàn)橹挥羞@一個(gè)方法嘛,那么使用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));
一個(gè)匿名內(nèi)部類對(duì)象就這樣被我們用lambda表達(dá)式改造成上面這樣子,我們可以這么理解,上面紅色加粗的代碼,就相當(dāng)于創(chuàng)建了一個(gè)Comparator對(duì)象并重寫了compare方法內(nèi)容.這時(shí)候,你該問了,這不就是一個(gè)匿名內(nèi)部類的語法糖嗎?事實(shí)上,從語法上看,的確很像一個(gè)語法糖,但二者之間存在很多顯著的差異.這里暫時(shí)不做深入討論.
在java8中引入了一個(gè)新的包java.util.function,這個(gè)包中定義了很多函數(shù)式接口用于支持lambda表達(dá)式,下面簡單介紹幾個(gè)常見接口.
Function接口,有一個(gè)參數(shù),并且返回一個(gè)結(jié)果,觀察該接口,我們發(fā)現(xiàn)Function能接受一個(gè)T類型的參數(shù)并返回R類型,這里使用泛型有更好的擴(kuò)展性.
@FunctionalInterfacepublic interface Function<T, R> { R apply(T t); }
下面我們使用Function接口來寫一個(gè)小例子,如下:
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); }
上述代碼中,我們定義了一個(gè)方法changeTheValue,接收兩個(gè)參數(shù),第一個(gè)是需要修改的value,第二個(gè)是一個(gè)function對(duì)象,在下面的測試方法中,我們根據(jù)自己的想法靈活的改變第二個(gè)參數(shù),可以得到對(duì)象的結(jié)果.運(yùn)行測試,可以得到result1:15 result2:300.
除了Function接口之外,類庫還提供了一個(gè)Consumer接口,他和Function接口唯一的區(qū)別就是,方法沒有返回值,還記得最上面我們遍歷集合的時(shí)候使用的list.forEach方法嗎,它接受的就是一個(gè)Consumer實(shí)例.
類庫給我們預(yù)定義的函數(shù)式接口,當(dāng)然不止上面提的這兩個(gè),還有一個(gè)Predicate,他返回一個(gè)boolean類型的值,用來判斷某項(xiàng)條件是否滿足,經(jīng)常用來進(jìn)行篩濾操作,其他接口這里也不再討論.
總結(jié)
我們初步認(rèn)識(shí)了lambda的簡單語法結(jié)構(gòu): 參數(shù)列表->lambda體
lambda的函數(shù)式接口(目標(biāo)類型),java8引進(jìn)了用于支持lambda表達(dá)式的java.util.Function接口
lambda表達(dá)式的應(yīng)用場景之一是替換之前廣泛使用的匿名內(nèi)部類的常規(guī)語法.
http://www.cnblogs.com/fingerboy/p/6768874.html