詳解Lambda

詳解Lambda

Java8已經(jīng)更新了好久了。變化很大,但感覺有用的不多。其中最廣為人知的就是Lambda表達式。看起來比較蛋疼,感覺Java越來越C化了。

當(dāng)初以為Lambda的作用就是為了簡化匿名內(nèi)部類的輸寫,最近看了些文章才發(fā)現(xiàn)是我自己膚淺了。Java更新一個大版本是又怎么會只是這樣小小的功能

Lambda表示式

Lambda的基本格式為()->內(nèi)容,以箭頭為分隔,左邊為參數(shù)區(qū),右邊為代碼區(qū).

可以把它看成是一個臨時定義的方法,沒有方法名,默認(rèn)為public,根據(jù)代碼區(qū)里的有沒有return來決定返回值.如果沒有return則是void.

  1. 單行代碼可以省略return
  2. 多行代碼使用{}來包裹.
  3. 代碼內(nèi)可以引用外部局部變量,實際上是隱式的把變量變成final

網(wǎng)上用得最多的例子就是(x,y)->x+y;,很蛋疼的例子,當(dāng)初看Junit單元測試時也是這樣的例子.

來個實際點的例子吧.

public boolean isEmpty(String text){
  return text==null||"".eqauls(text)
}
用Lambda是(text)->return text==null||"".eqauls(text);

看到這里估計很多人會覺得更蛋疼了.明明Lambda使用是的匿名內(nèi)部類的簡寫,為什么要拿方法來做比較,另外明明有方法可以調(diào)用,何必要用Lambda

Lambda的起源

曾經(jīng)無數(shù)次的羨慕js有閉包,可以把方法當(dāng)做參數(shù)來傳遞.在項目中有很多類都存在著很類似的方法

public String getIds(List<T> list){
if(list==null||list.isEmpty){
    return null;
}
    StringBuilder builder = new StringBuilder();
    for(T t:list){
    String id=doSomething(t)
    builder.append(",").append(id);
}
return builder.deleteCharAt(0).toString();
}

想抽取一下,奈何每個類里T不同,doSomething的實現(xiàn)也不同.T可以用泛型,doSomething(T t)這個方法怎么傳呢??

面向接口吧.我們可以定義一個接口來干這個事.

public interface GetId<T>{
  String getId(T t);
}

上面的方法就可以抽到工具類中了

public static <T> String getIds(List<T> list,GetId<T> interf){
  if(list==null||list.isEmpty){
    return null;
  }
  StringBuilder builder = new StringBuilder();
  for(T t:list){
    String id=interf.getId(t);
    builder.append(",").append(id);
  }
  return builder.deleteCharAt(0).toString();
}

說來說去好像不關(guān)Lambda什么的事呀.實際上上面的例子就是Lambda的起源.

隨著函數(shù)式編程廣為程序員喜愛,Java也在考慮加入函數(shù)式編程.但有兩個問題,一 做為一個強類型語言,類型轉(zhuǎn)換有點蛋疼,二 不能把方法當(dāng)成參數(shù)(有返回值的還好,void方法當(dāng)參數(shù)是相當(dāng)?shù)臒o語的).

Java考慮了半天,使用了上面的例子的邏輯來處理,用接口來包裝方法,把接口當(dāng)參數(shù)來代替.

上面的例子中.在類中調(diào)用如下

String ids=Utils.getIds(List<XXX> list,new GetId<XXX>(){
            getId(XXX x){
                return ooxx(x);
            }
});

這是一個參數(shù),如果是多個參數(shù)那不要嚇?biāo)廊耍?/p>

String ids=Utils.getIds(List<XXX> list,
   new GetId<XXX>(){
      getId(XXX x){
            return ooxx(x);
      }
    },
  new GetId<XXX>(){
    getId(XXX x){
      return xxoo(x);
    }
  },
  new GetId<XXX>(){
    getId(XXX x){
      return oxox(x);
    }
  }
);

所以Lambda第一個作用就體現(xiàn)出來了簡化書寫,好寫好看。如

String ids=Utils.getIds(List<XXX> list,(x)->ooxx(x));

這里有點奇怪x是什么的鬼???,其實完整的應(yīng)該是

String ids=Utils.getIds(List<XXX> list,(XXX x)->ooxx(x));

這里就體現(xiàn)了lambda的牛逼之處類型推導(dǎo)(準(zhǔn)確來說是Java8的特性),編譯時推導(dǎo)參數(shù)類型和返回類型.

最上面那個坑爹的例子估計很多人都會像我當(dāng)初一樣蒙B。(x,y)->x+y;,這x和y是什么的東東??

實際上這個例子應(yīng)該是(int x,int y)->return x+y;,這樣看就清楚多了.傳兩個int,返回其和.

再給個實際點的例子吧

String[] strings={"xx","oo","xxoo"};
傳統(tǒng)的排序是這樣的
Arrays.sort(strings,new Comparetor<String>(){
  public int compareTo(String s1,String s2){
    return s1.length-s2.length;
  }
})
使用lambda是這樣的
Arrays.sort(strings,(String s1,String s2)->return s1.length-s2.length);
或者
Arrays.sort(strings,(s1,s2)->s1.length-s2.length);
或者
Arrays.sort(strings,(s1,s2)->{
  int x= s1.length;
  int y=s2.length;
  return x-y;
  });

Lambda的實現(xiàn)

lambda的原理就是以把接口當(dāng)參數(shù)傳遞的方式來形成閉包.所以這個接口只能定義一個方法,這種接口叫函數(shù)接口.這種接口可以隱式轉(zhuǎn)為lambda

為了防止該接口的單方法性被破壞,Java8定義了一個注解FunctionInterface,Java庫中所有這種接口都已經(jīng)添加了這個注解。如

@FunctionInterface
public Interface Runable{
  void run();
}

當(dāng)然,自己也可以定義函數(shù)接口,很簡單,只要是單方法都可以。最好是加上注解,防止自己或別人去添加新的方法.

另外Java8中提供了很多常用的接口,免得自己去創(chuàng)建。這些接口放在java.utils.function包里.

默認(rèn)的接口可接收和返回的基本數(shù)據(jù)只有int,long,double三種,String視為對象。

  1. Custmer一家,提供了void類的接口,可以接收單個或兩個的基本數(shù)據(jù)類型或?qū)ο蟮膮?shù)(無參的有現(xiàn)成的Runable)。如
interface Custmer<T>{
  void apply(T t)
}

interface BiConsumer<T,U>{
  void apply(T t,U u);
}

另外還支持對象+基本數(shù)據(jù)類型的參數(shù)

  1. Predicate一家,提供了可接收1-2個基本數(shù)據(jù)類型或?qū)ο蟮膮?shù),返回boolean的接口(無參的是booleanSupplier接口)
  2. Function,Oprator,Supplier一家,提供了接收0-2個參數(shù),返回基本數(shù)據(jù)類型或?qū)ο蟮慕涌?/li>
  • Function一家可接收1-2個參數(shù),返回的是對象
  • Supplier一家不接收參數(shù),返回基本數(shù)據(jù)類型和對象
  • Operator一家接收1-2個參數(shù),返回同類型的數(shù)據(jù)。unary前綴的是接收一個參數(shù),binary前綴的是接收兩個參數(shù)。那個坑爹的(x,y)->x+y的例子就是由IntBinaryOperator接口來實現(xiàn)的.
interface IntBinaryOperator{
    int applyAsInt(int left,int right);
}

默認(rèn)提供的接口,如果是兩個基本數(shù)據(jù)類型的參數(shù),則參數(shù)的類型都必須是相同的.也就是說兩個以內(nèi)的參數(shù)基本上不用自己定義函數(shù)接口了.上面的例子就可以這樣寫

public static <T> String getIds(List<T> list,Function<T,String> interf){
 
}
使用時如下 
Utils.getIds(list,t->ooxx(t);)

函數(shù)式編程

函數(shù)式編程需要可以把函數(shù)當(dāng)成參數(shù),在Java中所有的東西都是需要有類型的,只有函數(shù)自身是沒有的。Lambda以間接的方式提供了函數(shù)的類型,及類型的推導(dǎo),為函數(shù)式編程清掃了最大的障礙。再配合新提供的Stream,在java中終與可以愉快的的使用函數(shù)式編程了。
然而....沒有Lambda,沒有Java8,我用Rxjava一樣可以愉快的函數(shù)式編程.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容