1. 程式人生 > >JAVA8給我帶了什麽——Optional和CompletableFuture

JAVA8給我帶了什麽——Optional和CompletableFuture

int orelse call() exec executors tac execution 是否 常常

不管是JAVA,還是.NET。我們常常會看到空異常(NullPointerException)。這種異常都是在運行的過程中出現。往往是變量是一個null值。但是你引用這個變量的後繼字段或是方法。所以我們代碼裏面常常會出現if (變量!=null)的相關操作。
如果你是一個.NET開發人員的話,那麽你一定知道.NET的可以為空的數據類型。同樣子java8引入了一個Optional類型,目地是為了決解為空帶來的一系列問題。Optional類提供了倆個靜態的方法

  • of方法:創建一個非空的Optional類型。
  • ofNullable方法:創建一個可以為空的Optional類型。

讓我們一起看一下用法吧。

 1 package com.aomi;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 import java.util.Optional;
 6 
 7 public class Main {
 8     private static Map<String, String> maps = new HashMap<String, String>();
 9 
10     public static void main(String[] args) {
11 // TODO Auto-generated method stub 12 13 maps.put("aomi", "val1"); 14 maps.put("key1", "val2"); 15 16 String val = getValue("aaa"); 17 18 Optional<String> optionalVal = Optional.ofNullable(val); 19 20 if(optionalVal.isPresent())
21 { 22 System.out.println(val.replace("a", "b")); 23 } 24 else 25 { 26 System.out.println("拿到變量值為空"); 27 } 28 } 29 30 public static String getValue(String key) { 31 if (maps.containsKey(key)) 32 return maps.get(key); 33 return null; 34 } 35 }

運行結果:

技術分享圖片

isPresent方法用於判斷當前的變量是否為空。從某意義上來講筆者覺得這好像並沒有多大的好處。同樣子我們要用isPresent來判斷是否為空。那麽跟寫if(變量!=null)有什麽分別。所以筆者打算換一換。

 1 package com.aomi;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 import java.util.Optional;
 6 
 7 public class Main {
 8     private static Map<String, String> maps = new HashMap<String, String>();
 9 
10     public static void main(String[] args) {
11         // TODO Auto-generated method stub
12 
13         maps.put("aomi", "val1");
14         maps.put("key1", "val2");
15 
16         String notNullVal  = getValue("aomi");
17         String nullVal  = getValue("aaa");
18         
19         Optional<String> optNotNullVal = Optional.ofNullable(notNullVal);
20         Optional<String> optNullVal = Optional.ofNullable(nullVal);
21         
22         System.out.println(optNotNullVal.orElse("拿到變量值為空"));
23         System.out.println(optNullVal.orElse("拿到變量值為空"));
24     }
25 
26     public static String getValue(String key) {
27         if (maps.containsKey(key))
28             return maps.get(key);
29         return null;
30     }
31 }

上面的代碼是這樣子的。筆者拿倆個變量,一個變量是為空的。一個不為空。然後筆者用Optional類的orElse來做文章。顯示如下。

技術分享圖片

當然Optional類裏面提供了幾個用於獲得值的方法。

  • get方法:就是用於獲得值,如果當前的Optional類是一個有值的變量,那麽就返回值。如果沒有的話,不好意思!他會報錯。
  • orElse方法:表示如果為空的話,我就返回方法給定的值。否則返回當前的值。
  • orElseGet方法:表示如果為空的話,執行一個回調的方法函數。你可以傳入一個lambda表達。
  • orElseThrow方法:表示如果為空的話,回返一個異常,可以是一個自定義的異常。

以上這些方法,筆者認為並不能說明Optional類的特別之處。如下

 1 package com.aomi;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 import java.util.Optional;
 6 
 7 public class Main {
 8     private static Map<String, String> maps = new HashMap<String, String>();
 9 
10     public static void main(String[] args) {
11         // TODO Auto-generated method stub
12 
13         maps.put("aomi", "val1");
14         maps.put("key1", "val2");
15 
16         String notNullVal = getValue("aomi");
17     
18 
19         Optional<String> optNotNullVal = Optional.ofNullable(notNullVal);
20 
21         Optional<String> optNullNewVal = optNotNullVal.map(ss -> ss.replace("a", "b"));
22 
23         System.out.println(optNullNewVal.orElse("拿到變量值為空"));
24     }
25 
26     public static String getValue(String key) {
27         if (maps.containsKey(key))
28             return maps.get(key);
29         return null;
30     }
31 }

運行結果:

技術分享圖片

我們可以到Optional類提供了一個map方法。這個功能跟以前講到的流的map有一點類似。你們可以看到筆者在上面通過map方法來把‘a‘字符替換為‘b’。最後val1變成為vbl1。如果筆者還想把‘l‘替換為’r‘。後面在增加一個Map如下

Optional<String> optNullNewVal = optNotNullVal.map(ss -> ss.replace("a", "b")).map(ss->ss.replace("l","r"));

即然有map方法了。是不是也主是有filter方法。沒有錯。還真的有。如下。

 1 package com.aomi;
 2 
 3 import java.util.HashMap;
 4 import java.util.List;
 5 import java.util.Map;
 6 import java.util.Optional;
 7 
 8 public class Main {
 9     private static Map<String, String> maps = new HashMap<String, String>();
10 
11     public static void main(String[] args) {
12         // TODO Auto-generated method stub
13 
14         maps.put("aomi", "vbl1");
15         maps.put("key1", "val2");
16     
17         String notNullVal = getValue("aomi");
18 
19         Optional<String> optNotNullVal = Optional.ofNullable(notNullVal);
20 
21         Optional<String> optNullNewVal = optNotNullVal.filter( ss->ss.contains("a")).map(ss->ss.replace("a", "b"));
22 
23         System.out.println(optNullNewVal.orElse("拿到變量值為空"));
24     }
25 
26     public static String getValue(String key) {
27         
28         if(maps.containsKey(key))
29             return maps.get(key);
30         return "map";
31     }
32 }

筆者找出含有’a‘字符的字串符。然後"a"替換 "b"。主要修改代碼倆個地方。如下

1.把val1修改為vbl1。主要是讓他有值,卻不含有‘a‘字符。了為證明他可以過濾掉有‘a’的字符串

maps.put("aomi", "vbl1");

2.增加filter方法進行過濾。條件必須含有‘a‘

.filter( ss->ss.contains("a"))

運行結果:

技術分享圖片

JAVA為了空值的問題增加了Optional類。提功能了一系列功能。大家可以試著用用感覺如何。

筆者記得好像是在JAVA5的時候,JAVA引一個Future接口。如果你沒有印像的話,那你們有沒有用到過FutureTask類呢。 以前要創建一個多線程的話,一般有倆種。一種是繼承Thread;一種是實現Runnable

 1 package com.aomi;
 2 
 3 public class Main {
 4 
 5     public static void main(String[] args) {
 6         // TODO Auto-generated method stub
 7 
 8         Thread th1 = new Thread() {
 9             @Override
10             public void run() {
11                 System.out.println("這是一個Thread副線程");
12             }
13         };
14         
15         th1.start();
16         
17         
18         Thread th2 = new Thread(new Runnable() {
19             
20             @Override
21             public void run() {
22                 System.out.println("這是一個Runnable副線程");
23             }
24         });
25         
26         th2.start();
27         
28         try {
29             Thread.sleep(2000);
30         } catch (InterruptedException e) {
31             // TODO Auto-generated catch block
32             e.printStackTrace();
33         }
34         
35         System.out.println("這是一個主線程");
36     }
37 
38 }

運行結果:

技術分享圖片

我們可以看到代碼中的倆種方式了吧。這倆個方式都只有一個毛病。沒有辦法實現返回值的功能。所以引入了Future接口。如下

 1 package com.aomi;
 2 
 3 import java.util.concurrent.Callable;
 4 import java.util.concurrent.ExecutionException;
 5 import java.util.concurrent.FutureTask;
 6 
 7 public class Fmain {
 8     public static void main(String[] args) {
 9 
10         FutureTask<String> task = new FutureTask<>(new Callable<String>() {
11             @Override
12             public String call() throws Exception {
13                 Thread.sleep(1000);
14                 return "i am aomi";
15             }
16         });
17 
18         new Thread(task).start();
19 
20         try {
21             System.out.println("主線程: 結果=" + task.get());
22         } catch (InterruptedException e) {
23             // TODO Auto-generated catch block
24             e.printStackTrace();
25         } catch (ExecutionException e) {
26             // TODO Auto-generated catch block
27             e.printStackTrace();
28         }
29 
30     }
31 }

運行結果:

技術分享圖片

我可以看到一個叫Callable接口。是裏面的call方法和Runnable方法有一點像。只是一個有返回值,一個沒有。FutureTask類同時也提了很多方法。比如上的代碼筆者在改改。加入判斷是否取消了。如果沒有取消的話,就取消掉他。然後也去獲取他的值。

 1 package com.aomi;
 2 
 3 import java.util.concurrent.Callable;
 4 import java.util.concurrent.ExecutionException;
 5 import java.util.concurrent.FutureTask;
 6 
 7 public class Fmain {
 8     public static void main(String[] args) {
 9 
10         FutureTask<String> task = new FutureTask<>(new Callable<String>() {
11             @Override
12             public String call() throws Exception {
13                 Thread.sleep(1000);
14                 System.out.println("副線程:返回值=i am aomi");
15                 return "i am aomi";
16             }
17         });
18 
19         new Thread(task).start();
20 
21         try {
22             if (!task.isCancelled())
23             {
24                 task.cancel(true);
25                 System.out.println("取消副線程");
26                 System.out.println("主線程: 結果=" + task.get());
27             }
28             else
29             {
30                 System.out.println("主線程: 結果=" + task.get());
31             }
32                 
33         } catch (InterruptedException e) {
34             // TODO Auto-generated catch block
35             e.printStackTrace();
36         } catch (ExecutionException e) {
37             // TODO Auto-generated catch block
38             e.printStackTrace();
39         }
40 
41     }
42 }

運行結果:

技術分享圖片

看到了沒有被取消了。同時你去獲得取消線程的結果時,會發生異常。有沒有.NET的程序員,感覺像不像.NET的任務(Task)。 事實上有上面的功能大部業務都可以實現了。但是JAVA8還是又引一個叫CompletableFuture類。相對於Future接口增加了很多方法。如下獲得異步裏面的結果。

 1 package com.aomi;
 2 
 3 import java.util.concurrent.CompletableFuture;
 4 import java.util.concurrent.ExecutionException;
 5 
 6 public class Cmain {
 7 
 8     public static void main(String[] args) {
 9         // TODO Auto-generated method stub
10         
11         CompletableFuture<String> future = new CompletableFuture<>();
12         
13         new Thread(new Runnable() {
14             
15             @Override
16             public void run() {
17                 
18                 try {
19                     Thread.sleep(2000);
20                 } catch (InterruptedException e) {
21                     // TODO Auto-generated catch block
22                     e.printStackTrace();
23                 }
24                 
25                 future.complete("i am a CompletableFuture");
26                 
27             }
28         }).start();
29         
30         
31         try {
32             
33             System.out.println(future.get());
34             
35         } catch (InterruptedException e) {
36             // TODO Auto-generated catch block
37             e.printStackTrace();
38         } catch (ExecutionException e) {
39             // TODO Auto-generated catch block
40             e.printStackTrace();
41         }
42 
43     }
44 
45 }

運行結果:

技術分享圖片

筆者在線程裏面睡了2000秒。所以你們運行之個例子的時候,會發現慢了2秒才顯示結果。說明future.get()會等線程的結果。事實上FutureTask類也是一樣子。所以CompletableFuture類提供一系列的功能組合。只要設計好的話,性能會提高很多。

 1 package com.aomi;
 2 
 3 import java.util.concurrent.CompletableFuture;
 4 import java.util.concurrent.ExecutionException;
 5 import java.util.concurrent.Executor;
 6 import java.util.concurrent.Executors;
 7 import java.util.concurrent.ThreadFactory;
 8 
 9 public class Dmain {
10 
11     public static void main(String[] args) {
12         // TODO Auto-generated method stub
13 
14         CompletableFuture<String> oneFuture = CompletableFuture.supplyAsync(() -> {
15 
16             System.out.println("supplyAsync用於新建");
17             return "2011";
18         });
19 
20         CompletableFuture<Long> twoFuture = oneFuture.thenApply((ss) -> {
21             System.out.println("thenApply用於轉化");
22             return Long.parseLong(ss);
23         });
24 
25         CompletableFuture<Long> threeFuture = twoFuture.thenCompose(val -> {
26             System.out.println("thenCompose用於組合倆個CompletableFuture,但是依賴上一個CompletableFuture");
27             try {
28                 Thread.sleep(2000);
29             } catch (InterruptedException e) {
30                 // TODO Auto-generated catch block
31                 e.printStackTrace();
32             }
33 
34             return CompletableFuture.supplyAsync(() -> 
35             {
36                 long result = val * 2;
37                 System.out.println("thenCompose的結果是:"+ result);
38                 return result;
39             }
40             
41             );
42         });
43 
44         CompletableFuture<String> otherFuture = CompletableFuture.supplyAsync(() -> {
45             System.out.println("用於thenCombine的測試  上面的結果+4");
46             return "4";
47         });
48 
49         CompletableFuture<Long> finalFuture = threeFuture.thenCombine(otherFuture, (arg1, arg2) -> {
50             return arg1 + Long.parseLong(arg2);
51         });
52 
53         finalFuture.thenAccept((ss) -> {
54             System.out.println("thenAccept用於處理相關的結果數據");
55         });
56 
57         finalFuture.thenRun(() -> {
58             System.out.println("thenRun用於異步完成,執行相關的操作");
59         });
60 
61         try {
62             
63             System.out.println(finalFuture.get());
64         } catch (InterruptedException e) {
65             // TODO Auto-generated catch block
66             e.printStackTrace();
67         } catch (ExecutionException e) {
68             // TODO Auto-generated catch block
69             e.printStackTrace();
70         }
71 
72     }
73 
74 }

運行結果:

技術分享圖片

這個個方法的作用筆者略微的列了出來。想要加深的話,你們可能最好在去找一些資料。關於CompletableFuture的教程網絡上很多。

JAVA8給我帶了什麽——Optional和CompletableFuture