您好,登錄后才能下訂單哦!
這篇文章主要講解了“Java的Lambda表達(dá)式使用實(shí)例分析”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Java的Lambda表達(dá)式使用實(shí)例分析”吧!
lambda 表達(dá)式 是Java 8新加入的新特性,它在Java中是引入了函數(shù)式編程這一概念。那么什么是函數(shù)式編程呢?
函數(shù)式編程:函數(shù)式編程是面向數(shù)學(xué)的抽象,將計(jì)算描述為一種表達(dá)式求值。
我們平常所說(shuō)的面向?qū)ο缶幊虒儆诿钍骄幊?/strong>,函數(shù)式編程和命令式編程的區(qū)別是:
函數(shù)式編程關(guān)心數(shù)據(jù)的映射,命令式編程關(guān)系解決問(wèn)題的步驟。
函數(shù)式編程關(guān)系類(lèi)型(代數(shù)結(jié)構(gòu))之間的關(guān)系,命令式編程關(guān)系解決問(wèn)題的步驟。
函數(shù)式編程的本質(zhì):
函數(shù)式編程中的函數(shù)指的不是計(jì)算機(jī)中的函數(shù),而是數(shù)學(xué)中的函數(shù),即自變量的映射。即:一個(gè)函數(shù)的值僅取決于函數(shù)參數(shù)的值,不依賴其他狀態(tài)。
嚴(yán)格意義上的函數(shù)式編程意味著不使用可變的變量,賦值,循環(huán)和其他命令式控制結(jié)構(gòu)進(jìn)行編程。
函數(shù)式編程的好處:
函數(shù)式編程的好處是主要是不可變性帶來(lái)的。沒(méi)有可變的狀態(tài),函數(shù)就是引用透明(Referential transparency)的和沒(méi)有副作用的(No Side Effect)。
上面這些都是一些基本的概念,但是我們平時(shí)可能接觸這些方面的東西比較少,所以一開(kāi)始感覺(jué)函數(shù)式編程是很難得東西。
Talk is cheap, show me the code!
先來(lái)一個(gè)最簡(jiǎn)單的例子,可能也是介紹的最多的例子了。哈哈!
給按鈕添加監(jiān)視器。
使用匿名內(nèi)部類(lèi)的方式,進(jìn)行添加。
submit.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(null, "點(diǎn)擊了確定按鈕", "確定", JOptionPane.INFORMATION_MESSAGE); } });
這種方式的缺點(diǎn):使用了很多的模板代碼,真正必要的代碼,只是方法體內(nèi)的代碼。所以,Java 8 引入的 Lambda 表達(dá)式可以簡(jiǎn)化這種代碼(當(dāng)然了,也是有限制的,不是所有的匿名內(nèi)部類(lèi)都可以,這個(gè)后面會(huì)提到。)。
使用 Lambda 表達(dá)式 簡(jiǎn)化代碼
submit.addActionListener((e)->{ JOptionPane.showMessageDialog(null, "點(diǎn)擊了確定按鈕", "確定", JOptionPane.INFORMATION_MESSAGE); });
Lambda 表達(dá)式是一個(gè)匿名方法,將行為像數(shù)據(jù)一樣進(jìn)行傳遞。
說(shuō)明
可以看出來(lái),使用 Lambda 表達(dá)式簡(jiǎn)化后的代碼表達(dá)變得更加清晰了,并且不用再去寫(xiě)繁瑣的模板代碼了。
進(jìn)一步簡(jiǎn)化
參數(shù)括號(hào)和代碼體的花括號(hào)也可以省略(只有一個(gè)參數(shù)時(shí),可以省略圓括號(hào),只有一行代碼時(shí),可以省略花括號(hào))。
ActionListener listener = e->JOptionPane.showMessageDialog(null, "點(diǎn)擊了確定按鈕", "確定", JOptionPane.INFORMATION_MESSAGE);
小結(jié)
當(dāng)使用 Lambda 表達(dá)式代替匿名內(nèi)部類(lèi)創(chuàng)建對(duì)象時(shí),Lambda 表達(dá)式的代碼塊將會(huì)替代實(shí)現(xiàn)抽象方法的方法體,Lambda 就相當(dāng)于一個(gè)匿名方法。
lambda 表達(dá)式由三部分組成:
形參列表。形參列表允許省略形參類(lèi)型。如果形參列表中只有一個(gè)參數(shù),可以省略形參列表的圓括號(hào)。
箭頭(->)。英文短線和大于號(hào)。
代碼塊。如果代碼塊只有一句,可以省略花括號(hào)。如果只有一條 return
語(yǔ)句,可以省略 return
,lambda表達(dá)式會(huì)自動(dòng)返回這條語(yǔ)句的值。
注:
之所以可以省略形參列表是因?yàn)?編譯器 可以進(jìn)行類(lèi)型推斷,例如:
List<Dog> dogs1 = new ArrayList<Dog>(); List<Dog> dogs2 = new ArrayList<>();
上面使用 菱形語(yǔ)法,可以省略尖括號(hào)里面的東西,這就是類(lèi)型推斷的作用。
但是類(lèi)型推斷也不是萬(wàn)能的,不是所有的都可以推斷出來(lái)的,所以有時(shí)候,還是要顯示的添加形參類(lèi)型,例如:
先不要管這個(gè)代碼的具體作用。
BinaryOperator b = (x, y)->x*y; //上面這句代碼無(wú)法通過(guò)編譯,下面是報(bào)錯(cuò)信息:無(wú)法將 * 運(yùn)算符作用于 java.lang.Object 類(lèi)型。 The operator * is undefined for the argument type(s) java.lang.Object, java.lang.Object
//添加參數(shù)類(lèi)型,正確的代碼。 BinaryOperator<Integer> b = (x, y)->x*y;
所以,類(lèi)型推斷不是萬(wàn)能的,如果編譯器無(wú)法推斷,那就是我們的錯(cuò)誤,不要過(guò)度依賴編譯器。有時(shí)候,顯示的添加參數(shù)類(lèi)型,還是很必要的,當(dāng)然了,這需要去多練習(xí)。
前面了解了,Lambda 表達(dá)式可以代替匿名內(nèi)部類(lèi),進(jìn)而達(dá)到簡(jiǎn)化代碼,表達(dá)清晰的目的。那么使用 Lambda 表示式的前提是什么呢?-- 函數(shù)式接口
Lambda 表達(dá)式的類(lèi)型,也被稱為 目標(biāo)類(lèi)型 (Target Type),它必須是一個(gè)函數(shù)式接口(Functional Interface)。所謂函數(shù)式接口,指的就是:只包含一個(gè)抽象方法的接口。(可以包含多個(gè)默認(rèn)方法,靜態(tài)方法,但必須只有一個(gè)抽象方法)。
注:Java 8 專(zhuān)門(mén)提供了一個(gè)注解:@FunctionalInterface
。用于標(biāo)注某個(gè)接口是函數(shù)式接口,這樣編譯時(shí)就會(huì)檢查,如果該接口含有多個(gè)抽象方法,編譯器就會(huì)報(bào)錯(cuò)。
上面使用 Lambda 表達(dá)式來(lái)為按鈕添加了監(jiān)視器,可以看出來(lái),Lambda 表達(dá)式 代替了 new ActionListener()
對(duì)象。
所以 Lambda 的表達(dá)式就是被當(dāng)成一個(gè)對(duì)象。
例如:
ActionListener listener = e->JOptionPane.showMessageDialog(null, "點(diǎn)擊了確定按鈕", "確定", JOptionPane.INFORMATION_MESSAGE);
從上面這個(gè)例子中可以看出來(lái),Lambda 表達(dá)式實(shí)現(xiàn)的是匿名方法–因此它只能實(shí)現(xiàn)特定函數(shù)式接口中的唯一方法。
所以 Lambda 表達(dá)式有下面兩種限制:
Lambda 表達(dá)式的目標(biāo)類(lèi)型必須是明確的函數(shù)式接口。 Lambda 表達(dá)式只能為函數(shù)式接口創(chuàng)建對(duì)象。Lambda只能實(shí)現(xiàn)一個(gè)方法,因此它只能為含有一個(gè)抽象方法的接口(函數(shù)式接口)創(chuàng)建對(duì)象。 介紹幾個(gè) Java 中重要的函數(shù)接口
從這種表可以看出來(lái),抽象方法的名字反而不是最重要的了,重要的是參數(shù)和返回值。 因?yàn)樵趯?xiě) Lambda 表達(dá)式的時(shí)候,也不要使用 抽象方法名了。
上面使用幾個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明上面接口的應(yīng)用:
測(cè)試代碼
import java.text.ParseException; import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Stream; public class Test { public static void main(String[] args) throws ParseException { //Lambda 表達(dá)式中的構(gòu)造器引用,用于簡(jiǎn)化代碼。 Creat<Dog> c = Dog::new; Dog dog = c.creat("小黑", 15); System.out.println(dog.toString()); Predicate<String> predicate = (words)->{ return words.length() > 20; }; assert predicate.test("I love you yesterday and today!") : "長(zhǎng)度小于20"; assert !predicate.test("God bless you!") : "長(zhǎng)度小于20"; System.out.println("------------------------"); Consumer<Dog> consumer = System.out::println; consumer.accept(dog); System.out.println("------------------------"); Function<Dog, String> function = (dogObj)->{ return dogObj.getName(); }; System.out.println(function.apply(dog)); System.out.println("------------------------"); Supplier<Dog> supplier = ()->{ return new Dog("大黃", 4); }; System.out.println(supplier.get()); //一元操作符 UnaryOperator<Boolean> unaryOperation = (flag)->{ return !flag; }; System.out.println(unaryOperation.apply(true)); BinaryOperator<Integer> binaryOperator = (x, y)->x*y; int result = binaryOperator.apply(999, 9999); System.out.println(result); } }
測(cè)試使用的實(shí)體類(lèi)
public class Dog { private String name; private int age; public Dog(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Dog [name=" + name + ", age=" + age + "]"; } }
自定義函數(shù)式接口
@FunctionalInterface public interface Creat<T> { public T creat(String name, int age); }
運(yùn)行截圖就不放了,感興趣的可以試一下。
說(shuō)明
我這里直接使用 Lambda 創(chuàng)建了對(duì)象,然后調(diào)用了這個(gè)對(duì)象的方法(就是lambda 的代碼塊部分),真正使用的時(shí)候,都是直接傳遞 Lambda 表達(dá)式的,這種方法并不推薦,但是可以讓我們很好的理解為什么? 可以看出來(lái),Lambda 表達(dá)式的作用,最后還是需要調(diào)用 重寫(xiě)的抽象方法的,只不過(guò)使用表達(dá)更加清晰,簡(jiǎn)化了代碼。
例如:
List<Dog> dogs = new ArrayList<>(); dogs.add(new Dog("大黃", 2)); dogs.add(new Dog("小黑", 3)); dogs.add(new Dog("小哈",1)); //將行為像數(shù)據(jù)一樣傳遞,使用集合的 forEach 方法來(lái)遍歷集合, //參數(shù)可以是一個(gè) Lambda 表達(dá)式。 Consumer<? super Dog> con = (e)->{ System.out.println(e); }; dogs.forEach(con); System.out.println("--------------------------\n"); //直接傳遞 Lambda 表達(dá)式,更加簡(jiǎn)潔 dogs.forEach(e->System.out.println(e)); System.out.println("--------------------------\n"); //使用方法引用,進(jìn)一步簡(jiǎn)化(可以看我的另一篇關(guān)于方法引用的博客) dogs.forEach(System.out::println); System.out.println("--------------------------\n"); //使用 Lambda 對(duì)集合進(jìn)行定制排序,按照年齡排序(從小到大)。 dogs.sort((e1, e2)->e1.getAge()-e2.getAge()); dogs.forEach(System.out::println);
可以看出來(lái),通過(guò)使用 Lambda 表達(dá)式可以,極大的簡(jiǎn)化代碼,更加方便的操作集合。值得一提的是:Lambda 表達(dá)式 和 Stream 的結(jié)合,可以擁有更加豐富的操作,這也是下一步學(xué)習(xí)的方向。
運(yùn)行截圖:
感謝各位的閱讀,以上就是“Java的Lambda表達(dá)式使用實(shí)例分析”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Java的Lambda表達(dá)式使用實(shí)例分析這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。