1. 程式人生 > >Java8新增的Lambda表示式

Java8新增的Lambda表示式

Lambda表示式支援將程式碼塊作為方法引數,Lambda表示式允許使用更簡單的程式碼來建立只有一個抽象方法的介面(這種介面被稱為函式式介面)的例項。

5.8.1 Lambda 表示式入門

Command.java

package code;
public interface Command
{
    // 接口裡定義的process()方法用於封裝“處理行為”
    void process(int[] target);
}

ProcessArray.java

package code;
public class ProcessArray
{
    public void
process(int[] target , Command cmd) { cmd.process(target); } }

CommandTest.java

package code;
public class CommandTest{
    public static void main(String [] args){
        ProcessArray pa = new ProcessArray();
        int [] target = {3,-4,6,4};
        pa.process(target,new Command(){
            public
void process(int [] target){ int sum = 0; for(int tmp : target){ sum += tmp; } System.out.println("陣列元素的總和是"+ sum); } }); } }

陣列元素的總和是9

CommandTest2.java

package code;
public class CommandTest2{
    public
static void main(String[] args){ ProcessArray pa = new ProcessArray(); int [] array = {3,-4,6,7}; pa.process(array, (int[] target)->{ int sum = 0; for(int tmp : target){ sum += tmp; } System.out.println("陣列元素的總和是" + sum); }); } }

Lambda表示式可以簡化匿名內部類物件,不需要new Xxx(){}這種繁瑣的程式碼,不需要指出重寫的方法名字,也不需要給出重寫的方法的返回值型別———只要給出重寫的方法括號以及括號裡的形參列表即可。
Lambda表示式的程式碼塊會代替實現抽象方法的方法體,Lambda表示式就相當一個匿名方法。
Lambda表示式由三個部分組成:

  • 形參列表。允許省略形參型別,如果形參列表中只有一個引數,甚至連形參列表的圓括號也可以省略。
  • 箭頭(->)
  • 程式碼塊。如果程式碼塊只包含一條語句,Lambda表示式允許省略程式碼塊的花括號,那麼這條語句就不要用花括號表示語句結束。Lambda程式碼塊只有一條return語句,甚至可以省略return關鍵字,Lambda表示式需要返回值,而他的程式碼塊中僅有一條省略了return的語句,Lambda表示式會自動返回這條語句的值。
    示例:LambdaQs.JAVA
package code;
interface Eatable{
    void taste();
}
interface Flyable{
    void fly(String weather);

}
interface Addable{
    int add(int a ,int b);
}
public class LambdaQs{
    public void eat(Eatable e){
        System.out.println(e);
        e.taste();
    }
    public void drive(Flyable f){
        System.out.println("我正在駕駛" + f);
        f.fly("碧空如洗的晴日");
    }
    public void test(Addable add){
        System.out.println("5+3的和為" + add.add(5,3));
    }
    public static void main(String[]args){
        LambdaQs lq = new LambdaQs();
        //Lambda表示式的程式碼塊只有一條語句,可以省略花括號
        lq.eat(()->System.out.println("蘋果的味道不錯"));
        //Lambda表示式的形參列表只有一個形參,可以省略圓括號
        lq.drive(weather->{
            System.out.println("今天天氣是"+ weather);
            System.out.println("直升機飛行平穩");
        });
        //Lambda表示式的程式碼塊只有一條語句,可以省略花括號
        //程式碼塊中只有一條語句,即使該表示式需要返回值,也可以省略return關鍵字
        lq.test((a,b)->a+b);
    }
}

code.LambdaQsLambda$1/[email protected]
蘋果的味道不錯
我正在駕駛code.LambdaQs$$Lambda$2/[email protected]
今天天氣是碧空如洗的晴日
直升機飛行平穩
5+3的和為8

5.8.2 Lambda 表示式與函式式介面

Lambda表示式的型別,也被稱為“目標型別(target type)”,Lambda表示式的目標型別必須是“函式式介面(functional interface)”。函式式介面代表只包含一個抽象方法的介面,函式式介面可以包含多個預設方法,類方法,但只能宣告一個抽象方法。
如果採用 匿名內部類語法來建立函式式介面的例項,則只需要實現一個抽象方法,在這種情況下即可採用Lambda表示式來建立物件。

java8專門為函式式介面提供了@FunctionalInterface註解,該註解通常放在介面定義前面,該註解對程式功能沒有任何作用,它用於告訴編譯器執行更嚴格的檢查—檢查該介面必須是函式式介面,否則編譯器就會報錯。
由於Lambda表示式的結果就是別當成物件,因此程式中完全可以使用Lambda表示式進行賦值。

Runnable r =()->{
    for(int i = 0;i<100;i++)
        System.out.println();
};

Lambda表示式有兩個限制。

  • Lambda表示式的目標型別必須是明確的函式式介面
  • Lambda表示式只能為函式式介面建立物件,Lambda表示式只能實現一個方法,因此它只能為只有一個抽象方法的介面建立物件。
Obj obj = ()->{
    for(int i=0;i<100;i++)
        System.out.println();
};

上面程式直接賦值給Object變數,編譯上面程式會報錯。

LambdaTest.java:33: 錯誤: 不相容的型別: Object 不是函式介面

所以Lambda表示式的目標型別必須是明確的函式式介面,而Lambda表示式的型別為Object,Object並不是函式式介面,因此上面程式碼報錯。
可以用上面三種常見的方式來保證程式碼正確:

  1. 將Lambda表示式賦值給函式式介面型別的引數傳給某個方法
  2. 將Lambda表示式作為函式式介面型別的引數傳給某個方法
  3. 使用函式式介面對Lambda表示式進行強制型別轉換
Object obj = (Runnable)->{
    for(int i = 0;i<100;i++)
        System.out.println();
}

同樣的Lambda表示式的目標型別完全可能是變化的—–唯一的要求是要與目標型別中唯一的抽象方法有相同的形參列表。

@FunctionalInterface
interface FKTest{
    void run();
}
Object obj2 = (FKTest)()->{
    for(int i= 0;i<100;i++)
        System.out.println();
};

Java8在java.util.function包下預定義了大量函式式介面。

  • XxxFunction
    • 包含一個apply()抽象方法,返回一個新值
  • XxxConsumer
    • 包含一個accept()抽象方法,不返回處理結果
  • XxxPredicate
    • 包含一個test()抽象方法,返回一個boolean值
  • XxxSupplier
  • 包含一個getAsXxx()抽象方法,返回一個數據

5.8.3 方法引用與構造器引用

方法引用和構造器引用可以讓Lambda表示式的程式碼更加簡潔。

種類 示例 對應的Lambda表示式
引用類方法 類名::類方法 (a,b…)->類名.類方法(a,b…)
引用特定物件的例項方法 特定物件::例項方法 (a,b…)->特定物件.例項方法(a,b…)
引用某類物件的例項方法 類名::例項方法 (a,b…)->a.例項方法(b…)
引用構造器 類名::new (a,b…)->類名(a,b…)

詳細:

  1. 引用類方法
@functionalInterface
interface Converter{
    Integer convert(String from);
}
public class MethodRefer{
    public static void main(String []args){
        //Converter con1 = from->Integer.valueOf(from);
        Converter con1 = Integer::valueOf;
        Integer val = con1.convert("22");
        System.out.println(val);
    }
}

對於上面的類方法引用,也就是呼叫Integer類的valueOf()類方法來實現Converter函式式介面中唯一的抽象方法,當呼叫Converter介面中的唯一的抽象方法時,呼叫引數將會傳給Integer類的valueOf()類方法。
2. 引用特定物件的例項方法
先用Lambda表示式來建立一個Converter物件

        Converter converter2 = from->"fkit.org".indexOf(from);
        Integer value = converter2.convert("it");
        System.out.println(value);

呼叫converter1物件的convert()方法將字串轉換為整數,

    Converter converter2 = "fkit.org"::indexOf;
        Integer value = converter2.convert("it");

對於上面的例項方法,也就是呼叫“fkit.org”物件的indexOf()例項方法,來實現,Converter函式式介面中唯一的抽象方法,呼叫引數將會傳給”fkit.org”物件的indexOf()例項方法。
3. 引用某類物件的例項方法

 @FunctionalInterface
interface MyTest{
    String test(String a,int b,int c);
}

該函式式介面中包含一個test()抽象方法,該方法負責根據Stirng,int,int三個引數生成一個String返回值。

    //MyTest mt = (a,b,c)->a.substring(b,c);
        MyTest mt = String::substring;
        String str = mt.test("Java I Love You",2,9);
        System.out.println(str);

建立Lambda表示式,
接著可以呼叫mt物件的test()—–由於mt物件是Lambda表示式建立的,test()方法執行體就是Lambda表示式的程式碼塊部分,因此上面程式輸出

va I Lo
對於上面的例項方法引用,也就是呼叫某個String物件的substring()例項方法來實現MyTest函式式介面中唯一的抽象方法,當呼叫MyTest介面中的唯一的抽象方法時,第一個呼叫引數將作為substring()方法的呼叫者,剩下的呼叫引數會作為substring()例項方法的呼叫引數。
4. 引用構造器

import javax.swing.*;
@FunctionalInterface
interface YourTest{
//JFrame需要匯入javax.swing.*;
    JFrame win(String title);
}

該函式介面包含一個win()抽象方法, 該方法負責根據String引數生成一個JFrame返回值。

    //YourTest yt = (String a) -> new JFrame(a);
        YourTest yt = JFrame::new;
        JFrame jf = yt.win("我的視窗");
        //System.out.println(jf);
        jf.setVisible(true);

使用Lambda表示式建立一個YourTest物件,
接著是呼叫yt物件的win()方法—-由於yt物件是Lambda表示式建立的,因此win()方法執行體就是Lambda表示式的程式碼塊部分,即執行體就是執行new JFrame(a);語句,並將這條語句的值作為方法的返回值。

呼叫YourTest物件的win()抽象方法時,實際只傳入了一個String型別的引數,這個String型別的引數會被傳給JFrame構造器—這就確定了是呼叫JFrame類的、帶一個String引數的構造器。

5.8.4 Lambda表示式與匿名內部類的聯絡和區別

Lambda表示式是匿名內部類的一種簡化,因此它可以部分取代匿名內部類的作用,Lambda和匿名內部類有以下的相同點

  • 都可以直接訪問“effectively final”的區域性變數,以及外部類的成員變數。
  • 兩個生成的物件一樣,都可以直接呼叫從介面中繼承的預設方法。
    LambdaAndInner.java
package code;
@FunctionalInterface
interface Displayable{
    //定義一個抽象方法和預設方法
    void display();
    default int add(int a,int b){
        return a+b;
    }
}
public class LambdaAndInner{
    private int age = 12;
    private static String name = "瘋狂軟體中心";
    public void test(){
        String book = "瘋狂Java講義";
        Displayable dis = ()->{
            System.out.println("book區域性變數為" + book);
            System.out.println("外部類的age例項變數為" + age);
            System.out.println("外部類的name類變數為" + name);

        };
        dis.display();
        System.out.println(dis.add(3,5));
    }
    public static void main(String [] args){
        LambdaAndInner lambda = new LambdaAndInner();
        lambda.test();
    }
}

book區域性變數為瘋狂Java講義
外部類的age例項變數為12
外部類的name類變數為瘋狂軟體中心
8

當程式使用Lambda表示式建立了Displayable的物件之後,該物件不僅可呼叫介面中唯一的抽象方法,也可呼叫介面中的預設方法。
主要存在如下區別:

  • 匿名內部類可以為任何介面建立例項—-不管介面包含多少個抽象方法,但是Lambda表示式只能為函式式介面建立例項。
  • 匿名內部類可以為抽象類甚至普通類建立例項;但Lambda表示式只能為函式式介面建立例項;
  • 匿名內部類實現的抽象方法的方法體允許呼叫介面中定義的預設方法,但Lambda表示式不允許呼叫介面中定義的預設方法
//嘗試呼叫介面中的預設方法,編譯器會報錯
System.out.println(add(3,5));

雖然Lambda比大師的目標型別:Displayable中包含了add()方法,但是Lambda表示式的程式碼塊不允許呼叫這個方法, 而改用匿名內部類的時候就可以呼叫這個add()方法。

5.8.5 使用Lambda表示式呼叫Arrays的類方法

Arrays類的有些方法需要Comparator、XxxOperato、XxxFunction等介面的例項,這些介面都是函式式介面,因此可以使用Lambda表示式來呼叫Arrays的方法。

package code;
import java.util.Arrays;
public class LambdaArrays{
    public static void main(String [] args){
        String [] arr1 = new String[]{
            "java","fkava","fkit","ios","android"};
        Arrays.parallelSort(arr1,(o1,o2)->o1.length()-o2.length());
        System.out.println(Arrays.toString(arr1));

        int[] arr2 = new int[]{3,-4,25,16,30,18};
        Arrays.parallelPrefix(arr2,(left,right)->left*right);
        System.out.println(Arrays.toString(arr2));

        long[] arr3 = new long[5];
        Arrays.parallelSetAll(arr3,operand->operand*5);
        System.out.println(Arrays.toString(arr3));
    }
}

[ios, java, fkit, fkava, android]
[3, -12, -300, -4800, -144000, -2592000]
[0, 5, 10, 15, 20]

  1. 第一段arr1的Lambda表示式的目標型別是Comparator,該Comparator指定了判斷字串大小的標準,字串越長,即可認為該字串越大;
  2. 第二段arr2的Lambda表示式的目標型別是IntBinaryOperator,該物件將會根據前後兩個元素來計算當前元素的值;
  3. 第三段arr3的Lambda表示式的目標型別是IntToLongFunction,該物件將會根據元素的索引來計算當前元素的值。

相關推薦

Java8特性----lambda表示式之Collection常見操作

現在越來越多的公司開始使用jdk8了,jdk8有許多新特性,其中一個特性便是流式處理,進而有好多對於集合的便利操作 我自己也是剛開始熟悉jdk8,便在此記錄一些基本的關於集合的操作 至於一些理論上的東西我就不寫了,某度一大堆,因為一點點介紹每段的含義來路也不是一篇部落格就能寫完的,我只會簡單說一下

我們一起來學java8lambda表示式Stream

Java 8 函數語言程式設計風格 Java 迄今為止最令人激動的特徵。這些新的語言特徵允許採用函式式風格來進行編碼,我們可以用這些特性完成許多有趣的功能。這些特性如此有趣以至於被認為是不合理的.他們說會影響計算速度,但是雖然是真的,但是存在皆合理. 所以我們摒棄缺點,研究優點. 演練  

初識Java8lambda表示式及Stream API

首先,引入一個模擬的專案,公司要求將年齡35歲以上的人員過濾出來。 將人員資料建模,有姓名、年齡、收入: public class Employee { private String name; private int age; private double salary; …

java8lambda表示式強化練習

本篇部落格寫2個練習,鞏固下lambda表示式,如果你還不瞭解lambda,請看這http://blog.csdn.net/zhiwenyan/article/details/70478193 先介紹下函式式介面,以後的部落格會著重介紹。 什麼是函式式介面 只包含一個

java8Lambda表示式入門

什麼是Lambda表示式? Lambda 是一個匿名函式,我們可以把 Lambda 表示式理解為是一段可以傳遞的程式碼(將程式碼像資料一樣進行傳遞)。可以寫出更簡潔、更靈活的程式碼。作為一種更緊湊的程式碼風格,使 Java的語言表達能力得到了提升。 匿名內部類的寫法 Comp

Java8lambda表示式和Stream API

Java8 的新特性:Lambda表示式、強大的 Stream API、全新時間日期 API、ConcurrentHashMap、MetaSpace。總得來說,Java8 的新特性使 Java 的執行速度更快、程式碼更少、便於並行、最大化減少空指標異常。 本篇部落格將以筆者

Java8-2-Lambda表示式實戰-一句話實現Map中按照Value排序

今天我們來實戰一把, 對Map的Value值排序進行簡化. 在以前的思路我們的做法如下: /** Map根據value排序; @param map @return*/public static <K, V extends Comparable<? super V

Java8Lambda表示式總結

// 1. 不需要引數,返回值為 5 () -> 5 // 2. 接收一個引數(數字型別),返回其2倍的值 x -> 2 * x // 3. 接受2個引數(數字),並返回他們的差值 (x, y) -> x – y // 4. 接收2個int型整數,返回他們的

Java 8新增Lambda表示式(6.8)

參考《Java瘋狂講義》 Lambda表示式支援將程式碼塊作為方法引數,Lambda表示式允許使用更簡潔的程式碼來建立只有一個抽象方法的介面(這種介面被稱為函式式介面)的例項 1. Lambda表示式入門 下面先使用匿名內部類來改寫(6.6介紹的命令模式Command表示式的例子)

Java8Lambda表示式簡介

  先闡述一下JSR(Java Specification Requests)規範,即Java語言的規範提案。是向JCP(Java Community Process)提出新增一個標準化技術規範的正式請求。任何人都可以提交JSR,可以向Java平臺增添新的API和服務。JSR已成為Java界的一個重要標準。可

Java8Lambda表示式的使用(看懂即可)

簡介 (譯者注:雖然看著很先進,其實Lambda表示式的本質只是一個"語法糖",由編譯器推斷並幫你轉換包裝為常規的程式碼,因此你可以使用更少的程式碼來實現同樣的功能。本人建議不要亂用,看懂別人的程式碼即可,因為這就和某些很高階的黑客寫的程式碼一樣,簡潔,難懂,難以除錯,維

Java8Lambda表示式增強版Comparator和排序

1、概述 在這篇教程裡,我們將要去了解下即將到來的JDK 8(譯註,現在JDK 8已經發布了)中的Lambda表示式——特別是怎樣使用它來編寫Comparator和對集合(Collection)進行排序。 首先,讓我們先定義一個簡單的實體類: pu

Java8Lambda表示式初步學習

Lambda表示式是java8新出的特性,lambda是一塊程式碼,由於java中沒有函式型別,我們不得不將實現特定功能的程式碼塊寫在匿名內部類中,早就有人調侃匿名內部類型別一行、方法名一行,真正方法內容一行,而有了lambad表示式,我們可以取代部分匿名內部類,減少多餘程式

IDEA無法編譯java8lambda表示式提示Error:(16, 48) java: -source 1.5 中不支援 lambda 表示式

http://www.07net01.com/2015/11/998227.html 在idea中新建了一個Java8的專案,但是寫lambda表示式提示語法錯誤,提示如下錯誤資訊: Error:(16, 48) java: -source 1.5 中不支援 lambda

java8Lambda表示式 4:MapReduce開發案例

簡介 通過Lambda中的Stream介面實現MapReduce工具,簡單理解就類似於sql之中的分組統計工具,只不過MapReduce是一種可以針對每個資料處理+集合的最終統計操作。 具體內容 集合不管怎麼改變,它一定是一個動態陣列,所以整個MapR

Java8之——Lambda表示式入門

簡介(譯者注:雖然看著很先進,其實Lambda表示式的本質只是一個"語法糖",由編譯器推斷並幫你轉換包裝為常規的程式碼,因此你可以使用更少的程式碼來實現同樣的功能。本人建議不要亂用,因為這就和某些很高階的黑客寫的程式碼一樣,簡潔,難懂,難以除錯,維護人員想罵娘.)Lambda

java8lambda表示式及方法引用(一)

當前很多公司的java開發環境都升級到jdk8以上了。lambda表示式是java8中最重要的更新,其目的是為了配合隨著並行運算流行起來的所謂“函式式”程式設計改進而來的語法糖。既然是語法糖,那麼其實不用這些lambda表示式也是可以實現原有功能的,只不過看起來程式碼行數多一

一、java8Lambda表示式

什麼是Lambda表示式 Lambda表示式是一段可以傳遞的程式碼。 λ表示式本質上是一個匿名方法。使用Lambda表示式可以使程式碼變的更加緊湊,例如在Java中實現一個執行緒,只輸出一個字串Hello World!,我們的程式碼如下所示: publi

Java8實戰—Lambda表示式

目錄 序言 Java8實戰中關於Lambda表示式的學習筆記 Lambda管中窺豹 Lambda可以簡單的理解為簡潔地表示可傳遞的匿名函式的一種方式: 它沒有名稱,但是有

JAVA 8 中新增 lambda 表示式的一些基本應用

import java.util.Arrays; import java.util.List; import java.uti