欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java8新特性之五:Optional

程序员文章站 2022-05-31 09:29:38
NullPointerException相信每个JAVA程序员都不陌生,是JAVA应用程序中最常见的异常。之前,Google Guava项目曾提出用Optional类来包装对象从而解决NullPointerException。受此影响,JDK8的类中也引入了Optional类,在新版的SpringD ......

  nullpointerexception相信每个java程序员都不陌生,是java应用程序中最常见的异常。之前,google guava项目曾提出用optional类来包装对象从而解决nullpointerexception。受此影响,jdk8的类中也引入了optional类,在新版的springdata jpa和spring redis data中都已实现了对该方法的支持。

1、optional类

 1 /**
 2  * a container object which may or may not contain a non-null value.
 3  * if a value is present, {@code ispresent()} will return {@code true} and
 4  * {@code get()} will return the value.
 5  *
 6  * @since 1.8
 7  */
 8 public final class optional<t> {
 9     /**
10      * common instance for {@code empty()}.
11      */
12     private static final optional<?> empty = new optional<>();
13 
14     /**
15      * if non-null, the value; if null, indicates no value is present
16      */
17     private final t value;
18 
19    // 其他省略
20 }

  该方法的注释大致意思是:optional是一个容器对象,它可能包含空值,也可能包含非空值。当属性value被设置时,ispesent()方法将返回true,并且get()方法将返回这个值。

  该类支持泛型,即其属性value可以是任何对象的实例。

2、optional类的方法

    序号    

方法

方法说明
1
private optional()
 无参构造,构造一个空optional
2
private optional(t value)
 根据传入的非空value构建optional
3
public static<t> optional<t> empty()
返回一个空的optional,该实例的value为空
4  
public static <t> optional<t> of(t value)
根据传入的非空value构建optional,与optional(t value)方法作用相同
5  
public static <t> optional<t> ofnullable(t value)

 与of(t value)方法不同的是,ofnullable(t value)允许你传入一个空的value,

当传入的是空值时其创建一个空optional,当传入的value非空时,与of()作用相同

6  
public t get()
 返回optional的值,如果容器为空,则抛出nosuchelementexception异常
7  
public boolean ispresent()
 判断当家optional是否已设置了值
8  
public void ifpresent(consumer<? super t> consumer)
 判断当家optional是否已设置了值,如果有值,则调用consumer函数式接口进行处理
9  
public optional<t> filter(predicate<? super t> predicate)
 如果设置了值,且满足predicate的判断条件,则返回该optional,否则返回一个空的optional
10  
public<u> optional<u> map(function<? super t, ? extends u> mapper)

 如果optional设置了value,则调用function对值进行处理,并返回包含处理后值的optional,否则返回空optional

11  
public<u> optional<u> flatmap(function<? super t, optional<u>> mapper)
 与map()方法类型,不同的是它的mapper结果已经是一个optional,不需要再对结果进行包装
12  
public t orelse(t other)
 如果optional值不为空,则返回该值,否则返回other 
13
public t orelseget(supplier<? extends t> other)
如果optional值不为空,则返回该值,否则根据other另外生成一个
14
public <x extends throwable> t orelsethrow(supplier<? extends x> exceptionsupplier)
throws x
如果optional值不为空,则返回该值,否则通过supplier抛出一个异常

3、optional类的方法举例

  《java 8 features tutorial – the ultimate guide》中给出了两个简单的例子,我们从这两个例子入手来简单了解一下optional容器的使用。

  示例一:

1 public static void main(string[] args) {
2   optional< string > fullname = optional.ofnullable( null );
3   system.out.println( "full name is set? " + fullname.ispresent() );
4   system.out.println( "full name: " + fullname.orelseget( () -> "[none]" ) );
5   system.out.println( fullname.map( s -> "hey " + s + "!" ).orelse( "hey stranger!" ) );
6 }

  运行结果:

full name is set? false
full name: [none]
hey stranger!  

  说明:

  ifpresent()方法当optional实例的值非空时返回true,否则返回false; 

  orelseget()方法当optional包含非空值时返回该值,否则通过接收的function生成一个默认的;

  map()方法转换当前optional的值,并返回一个新的optional实例;

  orelse()方法与orelseget方法相似,不同的是orelse()直接返回传入的默认值。

 

  示例二:修改示例一,使其生成一个非空值的optional实例

1 optional< string > firstname = optional.of( "tom" );
2 system.out.println( "first name is set? " + firstname.ispresent() );
3 system.out.println( "first name: " + firstname.orelseget( () -> "[none]" ) );
4 system.out.println( firstname.map( s -> "hey " + s + "!" ).orelse( "hey stranger!" ) );

  输出结果:

first name is set? true
first name: tom
hey tom!

  可以清晰地看出与示例一的区别。这不但简洁了我们的代码,而且使我们的代码更便于阅读。

  下面看一下例子中使用到的几个方法的源码:

  1)、of

1 public static <t> optional<t> of(t value) {
2   return new optional<>(value);
3}

  2)、ispresent

1 public boolean ispresent() {
2    return value != null;
3 }

  3)、orelseget

1 public t orelseget(supplier<? extends t> other) {
2     return value != null ? value : other.get();
3 }

  4)、orelse

1 public t orelse(t other) {
2     return value != null ? value : other;
3 }

  其他方法源码,读者可以去optional源码中查看。

4、使用optional避免空指针

  在我们日常开发过程中不可避免地会遇到空指针问题,在以前,出现空指针问题,我们通常需要进行调试等方式才能最终定位到具体位置,尤其是在分布式系统服务之间的调用,问题更难定位。在使用optional后,我们可以将接受到的参数对象进行包装,比如,订单服务要调用商品服务的一个接口,并将商品信息通过参数传入,这时候,传入的商品参数可能直接传入的就是null,这时,商品方法可以使用optional.of(t)对传入的对象进行包装,如果t为空,则会直接抛出空指针异常,我们看到异常信息就能立即知道发生空指针的原因是参数t为空;或者,当传入的参数为空时,我们可以使用optional.orelse()或optional.orelseget()方法生成一个默认的实例,再进行后续的操作。

  下面再看个具体例子:在user类中有个address类,在address类中有个street类,street类中有streetname属性,现在的需求是:根据传入的user实例,获取对应的streetname,如果user为null或address为null或street为null,返回“nothing found”,否则返回对应的streetname。

  实现一:

1 @data
2 public class user {
3     private string name;
4     private integer age;
5     private address address;
6 }
1 @data
2 public class address {
3     private street street;
4 }
1 @data
2 public class street {
3     private string streetname;
4     private integer streetno;
5 }
 1 public string getusersteetname(user user) {
 2 
 3     if(null != user) {
 4 
 5         address address = user.getaddress();
 6 
 7         if(null != address) {
 8 
 9             street street = address.getstreet();
10 
11             if(null != street) {
12                 return street.getstreetname();
13             }
14         }
15     }
16 
17     return "nothing found";
18 }

  实现二,使用optional:

   在实现一中明显的问题是if判断层级太深,下面复用optional改写:

1 @data
2 public class user {
3     private string name;
4     private integer age;
5     private optional<address> address = optional.empty();
6 }
1 @data
2 public class address {
3     private optional<street> street = optional.empty();
4 }
1 @data
2 public class street {
3     private string streetname;
4     private integer streetno;
5 }
1 public string getusersteetname(user user) {
2 
3     optional<user> useroptional = optional.ofnullable(user);
4     final string streetname = useroptional.orelse(new user()).getaddress().orelse(new address()).getstreet().orelse(new street()).getstreetname();
5     return stringutils.isempty(streetname) ? "nothing found" : streetname;
6 }

  利用orelse()方法给定默认值的方式确保不会报空指针问题问题,同时也能实现需求。