Java8-Lambda運算式(1)

來源:互聯網
上載者:User

標籤:很多   設計   als   樣本   選擇   類型檢查   sys   lin   lock   

  使用匿名類來表示不同的行為並不令人滿意:代碼十分的囉嗦,這會影響程式員在實踐中使用行為參數化的積極性。在這裡,我們會認識Java8中解決這個問題的工具Lambda運算式。它可以讓你很簡潔第表示一個行為或者傳遞代碼。現在你可以把Lambda運算式看做匿名功能,它基本上沒有聲明名稱的方法,但和匿名類一樣,它可以作為參數傳遞給一個方法。

1.Lambda管中窺豹

  我們可以把Lambda運算式理解為簡潔地表示課傳遞的匿名函數的一種方式:它沒有名稱,但它有參數列表、函數主體、傳回型別,可能還有可以拋出的異常列表。這個定義夠大的。讓我們慢慢道來。

(1).Lambda的介紹

  匿名--我們說,是因為它不像普通的方法那樣有一個明確的名稱:寫的少而想的多!

  函數--我們說它是函數,是因為Lambda函數不像方法那樣屬於某一個特定的類。但和方法一樣,Lambda有參數列表、函數主體、傳回型別,還可能含有可以拋出的異常列表。

  傳遞--Lambda運算式可以作為參數傳遞給方法或者儲存在變數中。

  簡潔--無需像匿名類那樣寫很多的代碼。

你是不是好奇Lambda這個詞是從哪兒來的?其實它是來自學術界開發出來的一套用來描述計算的λ演算法。你為什麼應該關心Lambda運算式呢?在這之前看到了,在Java中傳遞代碼十分的繁瑣和冗長。那麼,現在有了好訊息了!Lambda解決了這個問題:它可以讓你的代碼看上去十分的簡明。理論上來說,你在Java8之前做不了的事情,Lambda。但是,你現在用不著再用匿名類寫一堆笨重的代碼,來體驗行為參數化的好處吧!Lambda運算式鼓勵你採用我們之前提到的行為參數化風格。最終的結果就是你的代碼變得更加清晰、更加靈活。比如,利用Lambda運算式,你可以更為簡潔地自訂一個Comparator對象了。

 以前:

1         Comparator<Apple> byWeight = new Comparator<Apple>() {2 3             @Override4             public int compare(Apple o1, Apple o2) {5                 return o1.compareTo(o2);6             }7         }

之後(使用Lambda運算式):

1 Comparator<Apple> byWeight = (Apple o1, Apple o2) -> o1.compareTo(o2);

不得不承認,代碼看起來更加的清晰了!要是現在你覺得Lambda運算式看起來一頭霧水的話也沒關係,我們很快會一點一點的解釋清楚的。現在,請注意你基本上只傳遞了比較兩個比較重量所真正需要的代碼。看起來就像是只傳遞了compare方法的主體。你很快就會學到,你甚至還可以進一步簡化代碼。

  Lambda運算式有三個部分,如所示。

  參數列表--這個它採用了Comparator中compare方法的參數,兩個Apple。

  箭頭--箭頭->把參數列表和Lambda主體分割開。

  Lambda主體--比較兩個Apple的重量。運算式Lambda的傳回值了。

例如:

  A.(String s) -> s.length()  // 一個String類型的參數並返回一個int。Lambda沒有return語句,因為已經隱含了return。

  B.(Apple a) -> a.getWeight() > 150 //一個Apple類型的參數並返回一個boolean類型(蘋果的重量是否大於150g)。

  C.(int x, int y) -> {    //具有兩個int類型的參數而沒有傳回值(void 傳回值)。注意Lambda運算式可以包含多行語句,這裡是兩行。

  System.out.println("result:");

  System.out.println(x + y);

  }

  D.() -> 42 //沒有參數,並且返回一個int

(2).Lambda的文法

  Java語言設計者選擇這樣的文法,是因為C#和Scala等語言中的類似功能廣受歡迎。Lambda的基本文法是:

  (parameters) -> expression

 或者是(注意語句中的花括弧)

  (paramerers) -> { expression;}

注意:

 帶括弧與不帶括弧有一定的區別:不帶括弧只能是一行語句,並且不能帶return,末尾不能加分號;帶括弧可以是一行也可以是多行,但是如果函數有傳回值的話,在末尾必須return,而且每一句後面必須帶分號。例如:() -> "hello world"  等於 () -> { return "hello world";}

使用案例 Lambda樣本
布林運算式 (List<String> list) -> list.isEmpty()
建立對象 ()-> new Apple() 或者是 () -> {return new Apple();}
消費一個對象 (Apple a) -> { System.out.println( a.getWeight() );}
從一個對象中選擇/抽取 (String s) -> s.length()
組合兩個值 (int a, int b) -> a * b
比較兩個對象 (Apple a1, Apple a2) -> a1.compareTo(a2)

2.在哪裡以及如何使用Lambda

  現在你可能在想,在哪裡可以使用Lambda運算式。在上一個例子中,我們把Lambda賦給了一個Comparator<Apple> 類型的變數。我們在之前中實現filter方法中使用Lambda:

List<Apple> result = filterApple(inventory, (Apple apple) ->"red".equals(apple.getColor()));

 那到底哪裡可以使用Lambda呢?你可以在函數式介面上使用Lambda運算式。在之前的代碼中,你可以把Lambda運算式作為第二個參數傳遞給filter方法,因為它這裡需要Predicate<T>介面,而這個介面是一個函數式介面。如果聽起來太抽象了,不要太擔心了,現在我們就來詳細地解釋這是什麼意思,以及函數式介面是什麼。

(1).函數式介面

  還記得我們在之前,為了參數化filter方法的行為而建立的Predicate<T> 介面嗎? 它就是一個函數式介面,為什麼呢?因為Predicate僅僅定義了一個抽象方法:

1 public interface ApplePredicate<T> {2     boolean test(T t);3 }

  簡而言之,函數式介面就是只定義了一個抽象方法的介面。你已經知道Java API的一些函數式介面,如我們在之前涉及到的Comparator和Runnable

注意:

  介面現在還可以擁有預設方法(即在類沒有對方法進行實現時,其主體為方法提供了預設實現的方法)。哪怕很很多的預設的方法,只要介面只定義了一個抽象方法,它就仍然是一個函數是介面。

用函數式介面可以幹什麼呢?Lambda運算式允許你可以直接以內聯的形式為函數式介面的抽象方法提供了實現,並把整個運算式作為函數式介面的執行個體。你用匿名內部類也可以完成同樣的事情,只不過比較笨拙:需要提供一個實現,然後在直接內聯將它執行個體化。下面的代碼是有效,因為Runnable是一個只定義了一個抽象方法run的函數式介面:

A.使用Lambda

1 Runnable r1 = () -> System.out.println("Hello World");

B.使用匿名類

1         Runnable r2 = new Runnable() {2             3             @Override4             public void run() {5                 System.out.println("Hello World");6             }7         };

3.函數描述符

  (1).Lambda的簽名和函數式介面的抽象方法的簽名  

  函數式介面的抽象方法的簽名基本上就是Lambda運算式的簽名。我們將這種抽象方法叫作函數描述符。例如,Runnable介面可以看做一個什麼也不接受什麼也不返回(void)的函數的簽名(簽名表示為 () -> void ),因為他只有一個叫作run方法的抽象方法,這個方法什麼也不接受,什麼也不返回。

  我們在之前使用了一個特殊的標識符來描述Lambda和函數式介面的簽名。() -> void 代表參數列表為空白,並且返回void的函數。這正是Runnable介面所代表的。例如:(Apple, Apple) -> int 代表接收兩個Apple作為參數並且返回int的函數。

 你可能在想,Lambda運算式是怎麼做類型檢查的。之後我們會知道, 編譯器是如何檢查Lambda在給定的上下文中是否有效。現在,我們只需要知道Lambda運算式可以被賦給一個變數,或者傳遞給一個接收函數式介面作為參數的方法就好了,當然這個Lambda運算式的簽名要與函數式介面的抽象方法一樣的。

例如:

1     public void process(Runnable r){2         r.run();3     }
1 process(() -> System.out.println("Hello World"))

  上面的代碼列印的是 “Hello World”。Lambda運算式 () -> System.out.println("Hello World")不接收參數並且返回為void。這個恰恰就是Runnable介面中run方法的簽名。

(2)[email protected]註解

 如果你去看看新的Java API,會發現函數式介面帶有@FunctionalInterface的註解。這個註解用於表示該介面會設計成一個函數式介面。如果使用了@FunctionalInterface定義了一個介面,而它卻不是函數式介面的話,編譯器將返回一個提示原因的錯誤。例如,錯誤資訊可能是:Multiple non-overriding abstract methods found in interface Foo,表明存在多個抽象方法。注意的是:@FunctionalInterface不是必需的,但是對於為此設計的介面而言,使用它是比較好的做法。它就好像是@Override註解表示方法被重寫了

Java8-Lambda運算式(1)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.