背景

  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):

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xù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));

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

  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改造一下的模樣是這樣的.

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

   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));

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

 

  一個(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è)小例子,如下:

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

   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);

    }

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

  上述代碼中,我們定義了一個(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