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