天天看點

JAVA8給我帶了什麼——lambda表達

JAVA8給我帶了什麼——lambda表達

這此年來我一直從事.NET的開發。對于JAVA我内心深處還是很向往的。當然這并不是說我不喜歡.NET。隻是覺得JAVA也許才是筆者最後的歸處。

MK公司是以.NET起家的。而筆者也因為兄弟的原因轉行.NET。雖然有時候還是會拿起JAVA相關的知識回味一下。盡可能的不讓自己忘記。但是時代的進步卻把我狠狠甩到了後面去。

現在筆者終于離開了M公司。我想回去做JAVA,卻發現筆者已經跟不上JAVA時候。在筆者轉行.NET的時候,JAVA的版本才到 1.6。現在都1.8了。主要的是這個段時間發現很大的變化。是以就想看看JAVA8底能帶給我什麼。

筆者回來做JAVA就是想知道的第一件事——JAVA8裡面有什麼。不知道Oracle公司收了Sun公司之後為什麼一直沒有動作,在加上筆者忙着搞.NET開發。JAVA的事情就失去了資訊,對于JAVA7筆者不是沒有感覺,主要是JAVA8聽說變化很大。可以說是一個大版本的變化。是以筆者回來的時候就想知道——JAVA8裡面有什麼。同時筆者在這裡聲明這一系列主要是記得筆者自身從JAVA8得到了什麼,如果有要學JAVA8同學,本系列隻能作參考。

筆者是從事.NET的開發,相對JAVA以前而言。.NET有一些功能真的不錯。lambda表達示可以說成為.NET開發人員不可能離開的一部分。以前的JAVA可是沒有這個功能的。.NET可以把一個方法當做一個參數和變量來指派。JAVA在這一塊就弱了很多了。是以JAVA很多時候在設計模式上面做很大的展現。

把一個方法函數目前一個參數和變量來用的行為我們稱為行為參數化。那麼他有什麼好處呢?政策模式相信大家可能都聽過。不如筆者就以《JAVA實戰》這本書的例子為例吧。假設我是一個農戶,家裡種蘋果的。今年大豐收,好多蘋果。我把每一個蘋果都打标簽。并把相關蘋果的顔色,重量,大小,品種都記錄到資料庫中。

為了友善日後的檢視,筆者自己想一款軟體。在寫的過其中,筆者希望有這樣子功能——能以顔色來檢視相關的蘋果。是以筆者設計一個農民類,他有一個功能——根據顔色檢視蘋果。

Apple類:

複制代碼

1 package com.aomi;

2

3 public class Apple {

4 private String color;

5 private double weight;

6 private String typeName;

7 private int size;

8

9 public String getColor() {

10 return color;

11 }

12

13 public void setColor(String color) {

14 this.color = color;

15 }

16

17 public double getWeight() {

18 return weight;

19 }

20

21 public void setWeight(double weight) {

22 this.weight = weight;

23 }

24

25 public String getTypeName() {

26 return typeName;

27 }

28

29 public void setTypeName(String typeName) {

30 this.typeName = typeName;

31 }

32

33 public int getSize() {

34 return size;

35 }

36

37 public void setSize(int size) {

38 this.size = size;

39 }

40

41 @Override

42 public String toString() {

43 return "Apple [color=" + color + ", weight=" + weight + ", typeName=" + typeName + ", size=" + size + "]";

44 }

45

46

47

48 }

Peasant類

3 import java.util.ArrayList;

4 import java.util.List;

5

6 public class Peasant {

7

8 public List GetApplesByColor(List sources, String color) {

9

10 List suiteApples = new ArrayList<>();

11

12 for (Apple apple : sources) {

13

14 if (apple.getColor().equals(color))

15 suiteApples.add(apple);

17 }

18

19 return suiteApples;

20 }

21 }

Main:

6 public class Main {

8 public static void main(String[] args) {

9 // TODO Auto-generated method stub

10

11 // 查找紅色的蘋果

13 Peasant peasant = new Peasant();

14

15 List rApples = peasant.GetApplesByColor(getSources(), "red");

17 for (Apple apple : rApples) {

18 System.out.println(apple.toString());

19 }

21 }

22

23 public static List getSources() {

25 List sources = new ArrayList<>();

26 Apple apple1 = new Apple();

27

28 apple1.setColor("red");

29 apple1.setTypeName("hot");

30 apple1.setSize(12);

31 apple1.setWeight(34.2);

32 Apple apple2 = new Apple();

33

34 apple2.setColor("grayred");

35 apple2.setTypeName("hot");

36 apple2.setSize(12);

37 apple2.setWeight(34.2);

38

39 Apple apple3 = new Apple();

41 apple3.setColor("green");

42 apple3.setTypeName("hot");

43 apple3.setSize(12);

44 apple3.setWeight(34.2);

46 sources.add(apple1);

47 sources.add(apple2);

48 sources.add(apple3);

49

50 return sources;

51 }

52

53 }

運作結果:

寫完之後,感覺得很完美。過一段時間,突然發現好像不行。這個功能不好,我需要大小來檢視蘋果。于是修改一下,在Peasant類增加一個新的方法。根據大小來檢視:

1 public List GetApplesBySize(List sources, int size) {

3 List suiteApples = new ArrayList<>();

4

5 for (Apple apple : sources) {

6

7 if (apple.getSize() > size)

8 suiteApples.add(apple);

10 }

12 return suiteApples;

13 }

好吧。看起來也不錯,那麼有沒有想過後面還有可能會以重量來檢視蘋果。隻能在加一個方法了。那麼問題來了。一但功能多。整類會看起來一個點複雜。了解有一點難度。在沒有lambda表達的時候,JAVA會用一下有一點政策模式的方式實作。把相關的比較操作變成一個類。如下

Lookup接口類:

1 package com.aomi;

3 public interface Lookup {

4 boolean handle(Apple apple);

5 }

ColorRedLookup類:

3 public class ColorRedLookup implements Lookup {

5 @Override

6 public boolean handle(Apple apple) {

7 // TODO Auto-generated method stub

8 return apple.equals("red");

9 }

11 }

SizeLookup類:

3 public class SizeLookup implements Lookup {

8 return apple.getSize() > 120;

Peasant類:

8 public List LookupApple(List sources, Lookup lookup) {

14 if (lookup.handle(apple))

15 List rApples = peasant.LookupApple(getSources(), new ColorRedLookup());

上面的這種從某些方面來講筆者不是很喜歡。雖然這種方式看起來會比較人性化。但是相比筆者還是喜歡前面那一種增加方法的。這是個人的想法。

List rApples = peasant.LookupApple(getSources(), new ColorRedLookup());

看完這段代碼之後我們就可以發現一個問題。是不是每一個條件查找我都要建一個類呢?好像不是很好玩了。是以還是試一下我們試一下lambda表達。上面的代碼不用修改太多。隻要main方法裡面就可以了。

1 public static void main(String[] args) {

2 // TODO Auto-generated method stub

3

4 // 查找紅色的蘋果

6 Peasant peasant = new Peasant();

8 List rApples = peasant.LookupApple(getSources(), (Apple apple) -> apple.getColor().equals("red"));

10 for (Apple apple : rApples) {

11 System.out.println(apple.toString());

12 }

14 rApples = peasant.LookupApple(getSources(), (Apple apple) -> apple.getSize() > 120);

15

16 for (Apple apple : rApples) {

17 System.out.println(apple.toString());

18 }

19

是不是非常簡單呢。不用在建什麼類了。是以lambda表達的好處很明顯的。筆者想要什麼查找規則隻要改變一下規則就行了。如下用大小來查找。

List rApples = peasant.LookupApple(getSources(), (Apple apple) -> apple.getSize() > 12);

lambda表達給人感覺就是一個縮小版本的方法。往後面看的話,這種感覺你們會變的更加。但是在學習ambda表達的時候,有一些細節點還是要注意的。

文法點:(parameters)->expression或是(parameters)->{statements;}

學習會檢視lambda表達的簽名。即稱函數描述符

從文法點我們可以知道

左邊parameters:是表示參數,就好比如方法函數的參數是一樣子的。

中間->:是固定的。

右邊expression或是{statements;}:是表示隻能接受表達式,或是加大括号的語句。稱為主體

舉一些例子來加強一下吧

1.()-> {}//有效

2.()->"aomi"//有效

3.()->{return "aomi";}//有效

4.()->return "aomi "+ 1;//無效,主體是語句,要加上{}

5.()->{"aomi";}//跟上面的相反,主體是表達式,去掉{}

說到lambda表達的簽名,這邊就不得不提到一個概念函數式接口。他的定義是這樣子,隻要接口裡面隻有一個抽象方法都是可以算是函數式接口。舉一個JAVA是裡面的函數式接口

1 package java.lang;

3 @FunctionalInterface

4 public interface Runnable {

5 /**

6 * When an object implementing interface

Runnable

is used

7 * to create a thread, starting the thread causes the object's

8 *

run

method to be called in that separately executing

9 * thread.

10 *

11 * The general contract of the method

run

is that it may

12 * take any action whatsoever.

13 *

14 * @see java.lang.Thread#run()

15 */

16 public abstract void run();

17 }

Runnable就是一個函數式接口。他隻有一個run抽象方法。上面有一個注解類@FunctionalInterface他就是用于說明目前類是一個函數式接口。不過好像事實上你可以不用加上他。

AomiRunnable類:

3 public interface AomiRunnable {

5 void run();

7 }

1 public class Main {

3 public static void main(String[] args) {

4 Runnable run = () -> {

5 System.out.println("i am runnble");

6 };

8 AomiRunnable aRun = () -> {

9 System.out.println("i am aomirunnble");

10 };

12 run.run();

14 aRun.run();

16 }

17 }

看到上面的代碼不要奇怪。這個正好可以說明lambda表達的神奇之處。我們可以看到筆者定義了倆個函數式接口的變量。一個是JAVA裡面自帶的,一個是筆者自己寫的。倆個都可以正常的運作。可是筆者自已寫的好像沒有加入@FunctionalInterface。那是不是@FunctionalInterface沒有用呢?那還是有的。看下面就知道了。當你寫錯了就會提示你寫的不是函數式接口。

是以還是加上吧。這樣子顯得也專業一點嗎?

有了函數式接口,就必須說一下函數描述符。他事實上就是lambda表達的簽名。他是從哪裡來的呢?很簡單的,看接口的唯一方法就行了。就好例如上面Runnable類,他的方法就是無參數,無傳回值。你可寫才這樣子表示一下:()->{}。為什麼要有函數描述符呢?你們可以這樣子了解。JAVA裡面有很多自己寫好的函數式接口。如果你沒有函數描述符的話,你又何如明白什麼時候用到哪一個呢?如下

Predicate類:T -> boolean

Function類:T ->R

看到上面函數描述符的話,你是不是就可以知道他們的用法呢。是以了解函數描述符的話,你就可以很清楚的明白自己要用JAVA裡面的哪個函數式接口。同時還可以提高你在寫代碼過程的速度。目前JAVA裡面有哪一些函數式接呢?自己去看吧。在rt.jar裡面的

還是讓筆者再舉個例子吧。筆者希望按蘋果的大小來非序。是以我們可一定要用到List類的sort方法了。sort方法裡面以Comparator類作為參數。Comparator類是一個函數式接口。抽象方法如下

int compare(T o1, T o2);

是以函數描述符是(T,T)-> int。知道這些之後就好辦了。

3 List apples = getSources();

5 apples.sort((Apple a1, Apple a2) -> a2.getSize() - a1.getSize());

7 for (Apple apple : apples) {

9 System.out.println(apple);

12 }

看起很友善吧。

lambda表達的确不錯。使得用JAVA8開發的同學代碼更加的人性化。但是JAVA8還加入另一種功能叫方法引用。看一下例子。

10 List apples = getSources();

12 apples.sort(Main::AppleComparator);

14 for (Apple apple : apples) {

16 System.out.println(apple);

21 public static int AppleComparator(Apple a1, Apple a2) {

22 return a2.getSize() - a1.getSize();

25 public static List getSources() {

26

27 List sources = new ArrayList<>();

28 Apple apple1 = new Apple();

29

30 apple1.setColor("red");

31 apple1.setTypeName("hot");

32 apple1.setSize(13);

33 apple1.setWeight(34.2);

34 Apple apple2 = new Apple();

35

36 apple2.setColor("grayred");

37 apple2.setTypeName("hot");

38 apple2.setSize(12);

39 apple2.setWeight(34.2);

41 Apple apple3 = new Apple();

42

43 apple3.setColor("green");

44 apple3.setTypeName("hot");

45 apple3.setSize(14);

46 apple3.setWeight(34.2);

48 sources.add(apple1);

49 sources.add(apple2);

50 sources.add(apple3);

51

52 return sources;

53 }

54

55 }

主要修改的地方:

apples.sort(Main::AppleComparator);

增加的地方:

1 public static int AppleComparator(Apple a1, Apple a2) {

2 return a2.getSize() - a1.getSize();

3 }

在使用方法引用的時候,要注要一點,好像要靜态方法才行。如果不的話。會報錯的。

讓筆者好好說明下吧。方法引用并不是可以随便寫的。他是有依據的。總共有三種:

靜态方法,必須要符合(arg)->ClassName.staticMehtod(arg).的格式。

任意類型的執行個體方法,必須符合(object,rest)->object.instanceMethod(rest)的格式。

對象執行個體的執行個體方法,必須符合(args)->obj.instanceMethod(args)的格式。

我們可以看到方法引用就是針于單一方法的lambda表達的。 我們都知道Function函數接口的lambda表達的用法。好!假設筆者在Apple類中加入這樣子的方法

1 public int testApple(Apple a) {

2 return a.getSize() - 100;

然後在Main的代碼中是這樣子寫的。

Function fun = Apple::testApple;

不好意思他會報錯。

讓我們看一下Function的函數描述符吧。(T)-> R.好像跟testApple方法是一樣子的話,那為什麼不行呢? 讓我們把Function變成為他等同的一個lambda表達的寫吧。

(Apple a) -> 123//123可以是任意的數字。

跟筆者上面說的三點都不符合,當然不行了。是以想要可行的話,必須把testApple方法變成靜态的。這樣子就合适第一種了。關于比較Comparator類,JAVA8提供了一個comparing靜态方法。他接受了一個Function參數。并傳回一個Comparator類對象。修改一下。

apples.sort(comparing((Apple a) -> a.getSize()));

記得一個要引入

import static java.util.Comparator.comparing;

又因為方法引用的關系

(Apple a) -> a.getSize() 等于 Apple::getSize()

我們就可以把他修改為

apples.sort(comparing(Apple::getSize));

筆者用一個以前的例子吧。排序蘋果Main的類全部代碼

5 import static java.util.Comparator.comparing;

7 public class Main {

9 public static void main(String[] args) {

12 List apples = getSources();

14 apples.sort(comparing(Apple::getSize));

16 for (Apple apple : apples) {

17

18 System.out.println(apple);

23 public static int AppleComparator(Apple a1, Apple a2) {

24 return a2.getSize() - a1.getSize();

25 }

27 public static List getSources() {

29 List sources = new ArrayList<>();

30 Apple apple1 = new Apple();

31

32 apple1.setColor("red");

33 apple1.setTypeName("hot");

34 apple1.setSize(13);

35 apple1.setWeight(34.2);

36 Apple apple2 = new Apple();

37

38 apple2.setColor("grayred");

39 apple2.setTypeName("hot");

40 apple2.setSize(12);

41 apple2.setWeight(34.2);

43 Apple apple3 = new Apple();

44

45 apple3.setColor("green");

46 apple3.setTypeName("hot");

47 apple3.setSize(14);

48 apple3.setWeight(34.2);

50 sources.add(apple1);

51 sources.add(apple2);

52 sources.add(apple3);

53

54 return sources;

55 }

56

57 }

有了方法引用之後,在有一種叫構造引用的話,相信大家都不會有什麼吃驚的地方了。

Supplier app = Apple::new;

等于

Supplier app = ()->new Apple();

筆者就不多講了。

關于lambda表達的知識大部分是這樣子。筆者說實話吧。JAVA8加入lambda表達讓筆者一定也沒有感到興奮。因為.NET那邊都寫爛了。至少上面講到的知識讓筆者沒有什麼新鮮感。到是方法引用有一點味。但是下面的知識點卻讓筆者提了一點興趣了。

複合lambda表達。什麼意思!就是把多個lambda表達用or或and的概念放到一起使用。好比如上面的排序例子。可以反序的。修改下面的代碼

apples.sort(comparing(Apple::getSize).reversed());

加上.reversed()之後

沒有加之前

還有哦,還可以修改為

apples.sort(comparing(Apple::getSize).reversed().thenComparing(Apple::getWeight));

一個排序條件不夠,可以加哦。

讓我們換另外一些方式來看看吧。

Main類:

public static void main(String[] args) {

Function<Integer, Integer> add = (Integer a) -> a + 2;
    Function<Integer, Integer> multiply = (Integer a) -> a * 4;
    Function<Integer, Integer> andThen = add.andThen(multiply);
    Function<Integer, Integer> compose = add.compose(multiply);
    System.out.println("andThen結果:" + andThen.apply(2));
    System.out.println("compose結果:" + compose.apply(2));

}           

這個結果說明一個問題

andThen是multiply (add(x))。先執行了add,然後在multiply

compose是add(multiply(x))。先執行了multiply ,然後在add

對于andThen比較好了解。筆者不喜歡的是compose。為什麼?一般開發人員喜歡看其名知其意。compose的英文意思是構成,寫作,還有組成的意思。有一點難了解。

讓我們在看一個奇神的點吧。

5 import java.util.function.Predicate;

11 Predicate query = (Apple a) -> a.getSize() > 13;

13 query = query.and((Apple a) -> a.getWeight() < 20);

15 List apples = fliter(query);

17 for (Apple apple : apples) {

19 System.out.println(apple);

20 }

21

22 }

23

24 public static List fliter(Predicate pred) {

25 List nSources = new ArrayList<>();

26 List sources = getSources();

27 for (Apple apple : sources) {

28 if (pred.test(apple))

29 nSources.add(apple);

30 }

31 return nSources;

32 }

34 public static List getSources() {

36 List sources = new ArrayList<>();

37 Apple apple1 = new Apple();

39 apple1.setColor("red");

40 apple1.setTypeName("hot");

41 apple1.setSize(13);

42 apple1.setWeight(55.2);

43 Apple apple2 = new Apple();

45 apple2.setColor("grayred");

46 apple2.setTypeName("hot");

47 apple2.setSize(12);

48 apple2.setWeight(34.2);

50 Apple apple3 = new Apple();

52 apple3.setColor("green");

53 apple3.setTypeName("hot");

54 apple3.setSize(14);

55 apple3.setWeight(34.2);

57 Apple apple4 = new Apple();

58

59 apple4.setColor("green");

60 apple4.setTypeName("hot");

61 apple4.setSize(19);

62 apple4.setWeight(12.2);

63

64 sources.add(apple1);

65 sources.add(apple2);

66 sources.add(apple3);

67 sources.add(apple4);

68

69 return sources;

70 }

71

72 }

本來集合時面有四個蘋果,大小值大于13的有倆個蘋果。是以當我們把條件用and在加上的時候—— 重量小于20.結果隻有一個。

原文位址

https://www.cnblogs.com/hayasi/p/10621965.html