Java教程之Java8.0新特性之Lambda表達(dá)式:Java 8 已經(jīng)發(fā)布很久了,很多報(bào)道表明Java 8 是一次重大的版本升級(jí)。本篇文章,主要給大家介紹的是lambda表達(dá)式。 Lambda表達(dá)式Lambda表達(dá)式(也稱為閉包)是Java 8中最大和最令人期待的語(yǔ)言改變。它允許我們將函數(shù)當(dāng)成參數(shù)傳遞給某個(gè)方法,或者把代碼本身當(dāng)作數(shù)據(jù)處理:函數(shù)式開發(fā)者非常熟悉這些概念。 很多JVM平臺(tái)上的語(yǔ)言(Groovy、Scala等)從誕生之日就支持Lambda表達(dá)式,但是Java開發(fā)者沒有選擇,只能使用匿名內(nèi)部類代替Lambda表達(dá)式。 Lambda的設(shè)計(jì)耗費(fèi)了很多時(shí)間和很大的社區(qū)力量,最終找到一種折中的實(shí)現(xiàn)方案,可以實(shí)現(xiàn)簡(jiǎn)潔而緊湊的語(yǔ)言結(jié)構(gòu)。而lambda表達(dá)式的使用需要和函數(shù)式接口結(jié)合。 1.函數(shù)式接口1.1.概念 函數(shù)式接口在Java中是指:有且僅有一個(gè)抽象方法的接口。 函數(shù)式接口,即適用于函數(shù)式編程場(chǎng)景的接口。而Java中的函數(shù)式編程體現(xiàn)就是Lambda,所以函數(shù)式接口就是可 以適用于Lambda使用的接口。只有確保接口中有且僅有一個(gè)抽象方法,Java中的Lambda才能順利地進(jìn)行推導(dǎo)。 備注:“語(yǔ)法糖”是指使用更加方便,但是原理不變的代碼語(yǔ)法。例如在遍歷集合時(shí)使用的for-each語(yǔ)法,其實(shí) 底層的實(shí)現(xiàn)原理仍然是迭代器,這便是“語(yǔ)法糖”。從應(yīng)用層面來(lái)講,Java中的Lambda可以被當(dāng)做是匿名內(nèi)部 類的“語(yǔ)法糖”,但是二者在原理上是不同的。 1.2,格式 1. 修飾符 interface 接口名稱 { 2. public abstract 返回值類型 方法名稱(可選參數(shù)信息); 3. } 1.3 @FunctionalInterface注解 與 @Override 注解的作用類似,Java 8中專門為函數(shù)式接口引入了一個(gè)新的注解: @FunctionalInterface 。該注解可用于一個(gè)接口的定義上: 1. @FunctionalInterface 2. public interface MyFunctionalInterface { 3. void myMethod(); 4. } 一旦使用該注解來(lái)定義接口,編譯器將會(huì)強(qiáng)制檢查該接口是否確實(shí)有且僅有一個(gè)抽象方法,否則將會(huì)報(bào)錯(cuò)。需要注 意的是,即使不使用該注解,只要滿足函數(shù)式接口的定義,這仍然是一個(gè)函數(shù)式接口,使用起來(lái)都一樣. 2.函數(shù)式接口的使用2.1函數(shù)式接口作為參數(shù),方法不帶參數(shù) 1. //定義函數(shù)式接口 2. public interface MyInterface{ 3. 4. public abstract void show(); 5. 6. } 7. 8. //使用(匿名內(nèi)部類對(duì)象/函數(shù)式) 9. public class Demo01 { 10. 11. public static void main(String[] args) { 12. method01(new MyInterface01() { 13. 14. @Override 15. public void show() { 16. System.out.println("你好,函數(shù)式接口"); 17. } 18. }); 19. // 函數(shù)式 20. method01(() -> { 21. System.out.println("你好,函數(shù)式接口"); 22. }); 23. // 函數(shù)式簡(jiǎn)寫(如果方法體中只有一句代碼) 24. method01(() -> System.out.println("你好,函數(shù)式接口")); 25. } 26. 27. public static void method01(MyInterface01 inter) { 28. inter.show(); 29. } 30. 31. } 函數(shù)式接口的優(yōu)勢(shì) 2.2, 函數(shù)式接口作為參數(shù),方法帶參數(shù) 1. //定義函數(shù)式接口 2. public interface MyInterface02 { 3. 4. public abstract void show(String msg1, String msg2); 5. 6. } 7. 8. //使用函數(shù)式接口 9. public static void main(String[] args) { 10. //匿名內(nèi)部類對(duì)象 11. method01(new MyInterface02() { 12. 13. @Override 14. public void show(String msg1, String msg2) { 15. System.out.println(msg1 + msg2); 16. } 17. }); 18. //函數(shù)式完整 19. method01((String msg1, String msg2) -> { 20. System.out.println(msg1 + msg2); 21. }); 22. //函數(shù)式簡(jiǎn)寫 23. method01((msg1, msg2) -> System.out.println(msg1 + msg2)); 24. 25. } 26. 27. public static void method01(MyInterface02 inter) { 28. inter.show("hello", "函數(shù)式"); 29. } 2.3, 函數(shù)式接口作為返回值,方法不帶參數(shù) 1. //定義函數(shù)式接口 2. public interface MyInterface02 { 3. 4. public abstract void show(String msg1, String msg2); 5. 6. } 7. public static void main(String[] args) { 8. 9. getInter1().show("你好", "函數(shù)式"); 10. getInter2().show("你好", "函數(shù)式"); 11. 12. } 13. 14. // 函數(shù)式完整 15. public static MyInterface02 getInter1() { 16. 17. return (String msg1, String msg2) -> { 18. System.out.println(msg1 + msg2); 19. }; 20. } 21. 22. // 函數(shù)式簡(jiǎn)寫 23. public static MyInterface02 getInter2() { 24. 25. return (msg1, msg2) -> System.out.println(msg1 + msg2); 26. } 3.函數(shù)式編程應(yīng)用場(chǎng)景3.1,概念 在兼顧面向?qū)ο筇匦缘幕A(chǔ)上,Java語(yǔ)言通過(guò)Lambda表達(dá)式使用函數(shù)式接口,就叫做函數(shù)式編程 3.2, 使用lambada作為參數(shù) 如果拋開實(shí)現(xiàn)原理不說(shuō),Java中的Lambda表達(dá)式可以被當(dāng)作是匿名內(nèi)部類的替代品。如果方法的參數(shù)是一個(gè)函數(shù) 式接口類型,那么就可以使用Lambda表達(dá)式進(jìn)行替代。 1. public class Demo04Runnable{ 2. private static void startThread(Runnable task){ 3. new Thread(task).start(); 4. } 5. public static void main(String[] args) { 6. startThread(()‐>System.out.println("線程執(zhí)行")); 7. } 8. } 3.3, 使用函數(shù)式接口作為返回值 1. public class Demo06Comparator { 2. 3. private static Comparator<Integer> getComparator(){ 4. return (num1,num2)‐> num1 - num2; 5. } 6. 7. public static void main(String[] args) { 8. Integer[] array = {3,2,1}; 9. Arrays.sort(array, getComparator()); 10. //遍歷數(shù)組 11. } 12. } 3.4, 函數(shù)式接口的方法有返回值 1. public static void main(String[] args) { 2. 3. showMsg(new MyInterface03() { 4. 5. @Override 6. public String getMsg() { 7. return "hello functional interface"; 8. } 9. }); 10. 11. // lambada表達(dá)式 12. showMsg(() -> { 13. 14. return "hello1 functional interface"; 15. }); 16. 17. // lambda表達(dá)式簡(jiǎn)寫 18. showMsg(() -> "hello1 functional interface"); 19. 20. } 21. 22. public static void showMsg(MyInterface03 inter) { 23. String msg = inter.getMsg(); 24. System.out.println(msg); 25. } 4.常用函數(shù)式接口(Supplier接口)JDK提供了大量常用的函數(shù)式接口以豐富Lambda的典型使用場(chǎng)景,它們主要在 java.util.function 包中被提供。 下面是簡(jiǎn)單的幾個(gè)接口及使用示例。 4.1,Supplier接口 java.util.function.Supplier<T> 接口僅包含一個(gè)無(wú)參的方法: T get() 。用來(lái)獲取一個(gè)泛型參數(shù)指定類型的對(duì)象數(shù)據(jù)。由于這是一個(gè)函數(shù)式接口,這也就意味著對(duì)應(yīng)的Lambda表達(dá)式需要“對(duì)外提供”一個(gè)符合泛型類型的對(duì)象數(shù)據(jù) 4.2,基本使用 1. private static String getString(Supplier<String> function ){ 2. return function.get(); 3. } 4. public static void main(String[] args){ 5. String msgA="Hello"; 6. String msgB="World"; 7. System.out.println(getString(()->msgA+msgB)); 8. } 4.2,綜合案例 需求:使用 Supplier 接口作為方法參數(shù)類型,通過(guò)Lambda表達(dá)式求出int數(shù)組中的最大值。提示:接口的泛型請(qǐng)使用 java.lang.Integer 類。 1. public static void main(String[] args) { 2. Integer max = getMax(()->{ 3. Integer[] nums = {1,2,3,4}; 4. int max2 = nums[0]; 5. for (Integer num : nums) { 6. if(max2 < num){ 7. max2 = num; 8. } 9. } 10. return max2; 11. }); 12. System.out.println(max); 13. } 14. 15. public static Integer getMax(Supplier<Integer> supplier){ 16. return supplier.get(); 17. } 5.常用函數(shù)式接口(Consumer接口)5.1,Consumer接口 java.util.function.Consumer<T> 接口則正好與Supplier接口相反,它不是生產(chǎn)一個(gè)數(shù)據(jù),而是消費(fèi)一個(gè)數(shù)據(jù), 其數(shù)據(jù)類型由泛型決定 5.2,accept方法 Consumer 接口中包含抽象方法 void accept(T t) ,意為消費(fèi)一個(gè)指定泛型的數(shù)據(jù)。基本使用如: 1. public static void main(String[] args) { 2. consumeString((msg)->System.out.println(msg)); 3. } 4. 5. 6. public static void consumeString(Consumer<String> consumer){ 7. consumer.accept("hello"); 8. } 5.3, andThen方法 如果一個(gè)方法的參數(shù)和返回值全都是 Consumer 類型,那么就可以實(shí)現(xiàn)效果:消費(fèi)數(shù)據(jù)的時(shí)候,首先做一個(gè)操作, 然后再做一個(gè)操作,實(shí)現(xiàn)組合。而這個(gè)方法就是 Consumer 接口中的default方法 andThen 1. default Consumer<T> andThen(Consumer<? super T> after) { 2. Objects.requireNonNull(after); 3. return (T t) -> { accept(t); after.accept(t); }; 4. } 注: java.util.Objects 的 requireNonNull 靜態(tài)方法將會(huì)在參數(shù)為null時(shí)主動(dòng)拋出 NullPointerException 異常。這省去了重復(fù)編寫if語(yǔ)句和拋出空指針異常的麻煩。 需求:先打印大寫HELLO,再打印小寫hello 1. public static void main(String[] args) { 2. consumeString((msg) -> System.out.println(msg.toUpperCase()), 3. (msg) -> System.out.println(msg.toLowerCase())); 4. } 5. 6. public static void consumeString(Consumer<String> consumer1, Consumer<String> consumer2) { 7. consumer1.andThen(consumer2).accept("hello"); 8. } 6.常用函數(shù)式接口(Predicate接口)有時(shí)候我們需要對(duì)某種類型的數(shù)據(jù)進(jìn)行判斷,從而得到一個(gè)boolean值結(jié)果。這時(shí)可以使用 java.util.function.Predicate<T> 接口 6.1, test方法 Predicate 接口中包含一個(gè)抽象方法: boolean test(T t) 。用于條件判斷的場(chǎng)景 1. public enum SingleClass06 { 2. INSTANCE; 3. } 6.2,基本使用 1. public static void main(String[] args) { 2. System.out.println(predicateTest((msg) -> msg.length() > 3, "hello")); 3. } 4. 5. 6. public static boolean predicateTest(Predicate<String> predicate,String msg){ 7. return predicate.test(msg); 8. 9. } 7.總結(jié)在本文中,我們學(xué)會(huì)了使用lambda表達(dá)式的不同方式,同時(shí)也學(xué)習(xí)了java8.0開始自帶的一些常用函數(shù)式接口。 |
|
來(lái)自: 好程序員IT > 《Java培訓(xùn)教程》