• JAVA8给我带了什么——Optional和CompletableFuture


    不管是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的教程网络上很多。

  • 相关阅读:
    艾伟_转载:基于.NET平台的Windows编程实战(三)—— 项目的创建及主界面的设计 狼人:
    艾伟_转载:C# Design Patterns (2) Strategy 狼人:
    艾伟_转载:C# Design Patterns (5) Prototype 狼人:
    艾伟_转载:正则表达式30分钟入门教程 狼人:
    艾伟_转载:WCF安全之EndPointIdentity 狼人:
    艾伟_转载:老赵谈IL(3):IL可以看到的东西,其实大都也可以用C#来发现 狼人:
    艾伟_转载:.NET平台上的Memcached客户端介绍 狼人:
    艾伟_转载:关于.NET中的循环引用 狼人:
    艾伟_转载:VS 2008快捷键 狼人:
    艾伟_转载:Regex.Replace 方法的性能! 狼人:
  • 原文地址:https://www.cnblogs.com/hayasi/p/10660995.html
Copyright © 2020-2023  润新知