摘要:表達式的主要作用就是代替匿名內部類的煩瑣語法。從這點來看,表達式的代碼塊與匿名內部類的方法體是相同的。與匿名內部類相似的是,由于表達式訪問了局部變量,該局部變量相當于與一個隱式的修飾,因此不允許對局部變量重新賦值。
函數式接口
函數式接口(Functional Interface)就是一個只有一個抽象方法(可以包含多個默認方法或多個static方法)的普通接口,可以被隱式轉換為lambda表達式,可以現有的函數友好地支持 lambda。
函數式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
java.util.function
Lambda表達式入門匿名內部類:
public class CommandTest2 { 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); } }); } }
Lambda表達式:
public class CommandTest3 { public static void main(String[] args) { ProcessArray pa = new ProcessArray(); int[] array = {3, -4, 6, 4}; //處理數組,具體處理行為取決于匿名內部類 pa.process(array, (int[] target)->{ int sum = 0; for(int tmp : target) { sum += tmp; } System.out.println("數組元素的總和是:"+ sum); }); } }
當使用Lambda表達式代替匿名內部類創建對象時,Lambda表達式的代碼塊將會代替實現抽象方法的方法體,Lambda表達式就相當一個匿名方法。
Lambda表達式的主要作用就是代替匿名內部類的煩瑣語法。它由三部分組成。
形參列表。形參列表允許省略形參類型。如果形參列表只有一個參數,甚至連形參列表的圓括號也可以省略。
箭頭(->)。必須通過英文中畫線號和大于符號組成。
代碼塊。如果代碼塊只包含一條語句,Lambda表達式允許省略代碼塊的花括號,那么這條語句就不用用花括號表達語句結束。Lambda代碼塊只有一條return語句,甚至可以省略return關鍵字。Lambda表達式需要返回值,而它的代碼塊中僅有一條省略了return的語句,Lambda表達式會自動返回這條語句的值。
interface Eatable { void test(); } interface Flyable { void fly(String weather); } interface Addable { int add(int a, int b); } public class LambdaQs { //調用該方法需要Eatable對象 public void eat(Eatable e) { System.out.println(e); e.test(); } //調用該方法需要Flyable對象 public void drive(Flyable f) { System.out.println("老司機正在開:"+f); f.fly("亮瞎眼"); } //調用該方法需要Addable對象 public void test(Addable add) { System.out.println("34與59的和為:"+add.add(34, 59)); } public static void main(String[] args) { LambdaQs lq = new LambdaQs(); //Lamba表達式的代碼塊只有一條語句,可以省略花括號 lq.eat(()->System.out.println("蘋果很贊哦!")); //Lamba表達式的形參列表只有一個形參,可以省略圓括號 lq.drive(weather -> { System.out.println("今天天氣是:"+weather); System.out.println("直升機飛行平穩"); }); //Lambda表達式的代碼塊只有一條語句,可以省略花括號 //代碼塊中只有一條語句,即使該表達式需要返回值,也可以省略return關鍵字 lq.test((a, b) -> a + b); } }Lambda表達式與函數式接口
Lambda表達式的類型,也被稱為“目標類型”,Lambda表達式的目標類型必須是“函數式接口”。函數式接口代表只包含一個抽象方法的接口。函數式接口可以包含多個默認方法、類方法,但只能聲明一個抽象方法
如果采用匿名內部類語法來創建函數式接口的實例,則只需要實現一個抽象方法,在這種情況下即可采用Lambda表達式來創建對象,該表達式創建處理的對象的目標類型就是這個函數式接口。查詢Java8的API文檔,可以發現大量的函數式接口,例如:Runnable、ActionListener等接口都是函數式接口
@FunctionalInterface注解,該注解通常放在接口定義前面,該注解對程序功能沒有任何作用,它用于告訴編譯器執行更嚴格檢查——檢查該接口必須是函數式接口,否則編譯器就會報錯
Lambda表達式實現的是匿名方法——因此它只能實現特定函數式接口中的唯一方法。這意味著Lambda表達式有如下兩個限制:
Lambda表達式的目標類型必須是明確的函數式接口
Lambda表達式只能為函數式接口創建對象。Lambda表達式只能實現一個方法,因此它只能為只有一個抽象方法的接口(函數式接口)創建對象
為了保證Lambda表達式的目標類型是一個明確的函數式接口,可以有如下三種常見方式:
將Lambda表達式賦值給函數式接口類型的變量
將Lambda表達式作為函數式接口類型的參數傳給某個方法
使用函數式接口對Lambda表達式進行強制類型轉換
同樣的Lambda表達式的目標類型完全可能是變化的——唯一的要求是,Lambda表達式實現的匿名方法與目標類型(函數式接口)中唯一的抽象方法有相同的形參列表。
Java8在java.util.function包下預定義了大量函數式接口,典型地包含如下4類接口。
XxxFunction:這類接口中通常包含一個apply()抽象方法,該方法對參數進行處理、轉換(apply()方法的處理邏輯由Lambda表達式來實現),然后返回一個新的值。該函數式接口通常用于對指定數據進行轉換處理。
XxxConsumer:這類接口中通常包含一個accept()抽象方法,該方法與XxxFunction接口中的apply()方法基本相似,也負責對參數進行處理,只是該方法不會返回處理結果。
XxxxPredicate:這類接口中通常包含一個test()抽象方法,該方法通常用來對參數進行某種判斷(test()方法的判斷邏輯由Lambda表達式來實現),然后返回一個boolean值。該接口通常用于判斷參數是否滿足特定條件,經常用于進行篩濾數據。
XxxSupplier:這類接口中通常包含一個getAsXxx()抽象方法,該方法不需要輸入參數,該方法會按某種邏輯算法(getAsXxx()方法的邏輯算法由Lambda表達式來實現)返回一個數據。
方法引用與構造器引用如果Lambda表達式的代碼塊只有一條代碼,還可以在代碼塊中使用方法引用和構造器引用。
方法引用和構造器引用可以讓Lambda表達式的代碼塊更加簡潔。方法引用和構造器引用都需要使用兩個英文冒號。
種類 | 示例 | 說明 | 對應的Lambda表達式 |
---|---|---|---|
引用類方法 | 類名::類方法 | 函數式接口中被實現方法的全部參數傳給該類方法作為參數 | (a,b,...)->類名.類方法(a,b,...) |
引用特定對象的實例方法 | 特定對象::實例方法 | 函數式接口中被實現方法的全部參數傳給該類方法作為參數 | (a,b,...)->特定對象.實例方法(a,b,...) |
引用某類對象的實例方法 | 類名::實例方法 | 函數式接口中被實現方法的第一個參數作為調用者,后面的參數全部傳給該方法作為參數 | (a,b,...)->a.實例方法(b,...) |
引用構造器 | 類名::new | 函數式接口中被實現方法的全部參數傳給該類方法作為參數 | (a,b,...)->new 類名(a,b,...) |
@FunctionalInterface interface Converter{ Integer convert(String from); }
該函數式接口包含一個convert()抽象方法,該方法負責將String參數轉換成Integer。下面代碼使用Lambda表達式來創建Converter對象。
//下面代碼使用Lambda表達式創建Converter對象 Converter converter1 = from -> Integer.valueOf(from);
上面Lambda表達式的代碼塊只有一條語句,因此程序省略了該代碼塊的花括號;而且由于表達式所實現的convert()方法需要返回值,因此Lambda表達式將會把這條代碼的值作為返回值
調用convert1對象的convert()方法將字符串轉換為整數了,例如如下代碼
Integer val = converter1.convert("99"); System.out.println(val); // 輸出整數99
上面Lambda表達式的代碼塊只有一行調用類方法的代碼,因此可以使用如下方法引用進行替換
// 方法引用代替Lambda表達式:引用類方法。 // 函數式接口中被實現方法的全部參數傳給該類方法作為參數。 Converter converter1 = Integer::valueOf;
對于上面的類方法引用,也就是調用Integer類的valueOf()類方法來實現Converter函數式接口中唯一的抽象方法,當調用Converter接口中唯一的抽象方法時,調用參數將會傳給Integer類的valueOf()類方法。
引用特定對象的實例方法// 下面代碼使用Lambda表達式創建Converter對象 Converter converter2 = from -> "fkit.org".indexOf(from); Integer value = converter2.convert("it"); System.out.println(value); // 輸出2
// 方法引用代替Lambda表達式:引用特定對象的實例方法。 // 函數式接口中被實現方法的全部參數傳給該方法作為參數。 Converter converter2 = "fkit.org"::indexOf;
對于上面的實例方法引用,也就是調用"fkit.org"對象的indexOf()實例方法來實現Converter函數式接口中唯一的抽象方法,當調用Converter接口中唯一的抽象方法時,調用參數將會傳給"fkit.org"對象的indexOf()實例方法。
引用某類對象的實例方法定義如下函數式接口
@FunctionalInterface interface MyTest { String test(String a , int b , int c); }
使用Lambda表達式來創建一個MyTest對象
MyTest mt = (a , b , c) -> a.substring(b , c); String str = mt.test("Java I Love you" , 2 , 9); System.out.println(str); // 輸出:va I Lo
// 方法引用代替Lambda表達式:引用某類對象的實例方法。 // 函數式接口中被實現方法的第一個參數作為調用者, // 后面的參數全部傳給該方法作為參數。 MyTest mt = String::substring;引用構造器
@FunctionalInterface interface YourTest { JFrame win(String title); }
下面代碼使用Lambda表達式創建YourTest對象
YourTest yt = (String a) -> new JFrame(a);
JFrame jf = yt.win("我的窗口"); System.out.println(jf);
// 構造器引用代替Lambda表達式。 // 函數式接口中被實現方法的全部參數傳給該構造器作為參數。 YourTest yt = JFrame::new;
對于上面的構造器引用,也就是調用某個JFrame類的構造器來實現YourTest函數式接口中唯一的抽象方法,當調用YourTest接口中的唯一的抽象方法時,調用參數將會傳給JFrame構造器。調用YourTest對象的win()抽象方法時,實際只傳入了一個String類型的參數,這個String類型的參數會被傳給JFrame構造器——這就確定了調用JFrame類的、帶一個String參數的構造器。
Lambda表達式與匿名內部類的聯系和區別Lambda表達式是匿名內部類的一種簡化,存在如下相同點
Lambda表達式與匿名內部類一樣,都可以直接訪問“effectively final”的局部變量,以及外部類的成員變量(包括實例變量和類變量)。
Lambda表達式創建的對象與匿名內部類生成的對象一樣,都可以直接調用從接口中繼承的默認方法。
@FunctionalInterface interface Displayable { //定義一個抽象方法和默認方法 void display(); default int add(int a, int b) { return a + b; } } public class LambdaAndInner { private int age = 24; private static String name = "簡單點,說話的方式簡單點" ; public void test() { String sing = "演員"; Displayable dis = () -> { //訪問“effectively final”的局部變量 System.out.println("sing局部變量為:"+ sing); //訪問外部類的實例變量和類變量 System.out.println("外部類的age實例變量為:"+ age); System.out.println("外部類的name類變量為:"+ name); }; dis.display(); //調用dis對象從接口中繼承的add()方法 System.out.println(dis.add(34, 59)); } public static void main(String[] args) { LambdaAndInner lambdaAndInner = new LambdaAndInner(); lambdaAndInner.test(); } }
上面Lambda表達式創建了一個Display的對象,Lambda表達式的代碼塊中的三行粗體字代碼分別示范了訪問“effectively fianl”的局部變量、外部類的實例變量和類變量。從這點來看,Lambda表達式的代碼塊與匿名內部類的方法體是相同的。
與匿名內部類相似的是,由于Lambda表達式訪問了sing局部變量,該局部變量相當于與一個隱式的final修飾,因此不允許對sing局部變量重新賦值。
Lambda表達式與匿名內部類主要存在如下區別
匿名內部類可以為任意接口創建實例——不管接口包含多少個抽象方法,只有匿名內部類實現所有的抽象方法即可;但Lambda表達式只能為函數式接口創建實例。
匿名內部類可以為抽象類甚至普通類創建實例;但Lambda表達式只能為函數式接口創建實例。
匿名內部類實現的抽象方法的方法體允許調用接口中定義的默認方法;但Lambda表達式的代碼塊不允許調用默認方法。
使用Lambda表達式調用Arrays的類方法Arrays類的有些方法需要Comparator、XxxOperator、XxxFunction等接口的實例,這些接口都是函數式接口,因此可以使用Lambda表達式來調用Arrays的方法
import java.util.Arrays; import javax.management.openmbean.OpenDataException; public class LambdaArrays { public static void main(String[] args) { String arr1[] = new String[]{"皇家馬德里", "巴塞羅那", "巴黎圣日耳曼","尤文圖斯","切爾西"}; Arrays.parallelSort(arr1, (o1, o2) -> o1.length() - o2.length()); System.out.println(Arrays.toString(arr1)); int[] arr2 = new int[]{4, 2, 1, 3, 5}; //left代表數組中前一個索引處的元素,計算第一個元素時,left為1 //right代表數組中當前索引處的元素 Arrays.parallelPrefix(arr2, (left, right) -> left * right); System.out.println(Arrays.toString(arr2)); long[] arr3 = new long[5]; //operand代表正在計算的元素索引 Arrays.parallelSetAll(arr3, operand -> operand * 5); System.out.println(Arrays.toString(arr3)); } }
(o1, o2) -> o1.length() - o2.length():目標類型是Comparator指定了判斷字符串大小的標準:字符串越長,即可認為該字符串越大
(left, right) -> left * right:目標類型是IntBinaryOperator,該對象將會根據前后兩個元素來計算當前元素的值
operand -> operand * 5::目標類型是IntToLongFunction,該對象將會根據元素的索引來計算當前元素的值
Lambda表達式可以讓程序更加簡潔。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66304.html
摘要:在支持一類函數的語言中,表達式的類型將是函數。匿名函數的返回類型與該主體表達式一致如果表達式的主體包含一條以上語句,則表達式必須包含在花括號中形成代碼塊。注意,使用表達式的方法不止一種。 摘要:此篇文章主要介紹 Java8 Lambda 表達式產生的背景和用法,以及 Lambda 表達式與匿名類的不同等。本文系 OneAPM 工程師編譯整理。 Java 是一流的面向對象語言,除了部分簡...
摘要:表達式簡介表達式是一個匿名函數對于而言并不很準確,但這里我們不糾結這個問題。如果表達式的正文有一條以上的語句必須包含在大括號代碼塊中,且表達式的返回值類型要與匿名函數的返回類型相同。 版權聲明:本文由吳仙杰創作整理,轉載請注明出處:https://segmentfault.com/a/1190000009186509 1. 引言 在 Java 8 以前,若我們想要把某些功能傳遞給某些方...
摘要:初體驗下面進入本文的正題表達式。接下來展示表達式和其好基友的配合。吐槽一下方法引用表面上看起來方法引用和構造器引用進一步簡化了表達式的書寫,但是個人覺得這方面沒有的下劃線語法更加通用。 感謝同事【天錦】的投稿。投稿請聯系 tengfei@ifeve.com 本文主要記錄自己學習Java8的歷程,方便大家一起探討和自己的備忘。因為本人也是剛剛開始學習Java8,所以文中肯定有錯誤和理解偏...
摘要:語言是強類型面向對象的語言,所以必須提供一種數據類型作為表達式的返回值類型符合中函數格式的定義符合面向對象規則,所以最終表達式要有一個映射成對象的過程。定一個函數式接口我們在接口里定義了一個沒有參數返回值的抽象方法。 在JAVA中,Lambda 表達式(Lambda expression)是一個抽象方法的實現。這個抽象方法必須是在接口中聲明的,而且實現類只需要實現這一個抽象方法,我們稱...
摘要:表達式又名閉包匿名函數筆記根據終于在中引入了表達式。函數式接口要介紹中表達式的實現,需要知道什么是函數式接口。但同樣需要保證外部的自由變量不能在表達式中被改變。 Java Lambda 表達式(又名閉包 (Closure)/ 匿名函數 ) 筆記 根據 JSR 335, Java 終于在 Java 8 中引入了 Lambda 表達式。也稱之為閉包或者匿名函數。 showImg(https...
閱讀 1393·2021-11-22 15:11
閱讀 2838·2019-08-30 14:16
閱讀 2755·2019-08-29 15:21
閱讀 2914·2019-08-29 15:11
閱讀 2451·2019-08-29 13:19
閱讀 2985·2019-08-29 12:25
閱讀 417·2019-08-29 12:21
閱讀 2829·2019-08-29 11:03