Java8中Optional使用详解,优雅的消灭空指针异常

一、概述

为了避免编写代码的时出现很多的空指针异常。在很多情况下我们都需要做各种非空的判断。

比如如下代码:

// 用户对象先获取地址对象,再用地址对象获取省的值
user.getAddress().getProvince();

如果usernull或者user.getAddress()null的时候,就会报空指针异常(NullPointerException)。

为了避免空指针异常导致程序终止,一般就会这么写:

if(user!=null){
    
    
    Address address = user.getAddress();
    if(address!=null){
    
    
        String province = address.getProvince();
    }
}

尤其是对象中的属性还是一个对象的情况下。这种判断会更多。而过多的判断语句会让我们的代码显得臃肿不堪。

所以在JDK8中引入了Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。

并且在很多函数式编程相关的API中也都用到了Optional,如果不会使用Optional也会对函数式编程的学习和

使用造成一定影响。

二、创建Optional对象

Optional(T value),即Optional的构造函数,它是private权限的,不能由外部调用的。

Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空。

因此一般获取一个Optional对象的方式,都是借助如下方法。

2.1 ofNullable

一般使用Optional静态方法ofNullable来把数据封装成一个Optional对象。

无论传入的参数是否为null都不会出现问题。

User user = new User();
Optional<User> userOptional = Optional.ofNullable(user);

这里可能会觉得还要加一行代码来封装数据比较麻烦。

但是在实际开发中我们的数据很多是从数据库获取的。Mybatis3.5版本开始也已经支持Optional了。

可以直接把dao方法的返回值类型定义成Optional类型,MyBastis会自己把数据封装成Optional对象返回。

封装的过程也不需要自己操作。

2.2 of

如果能够确定一个对象不是空的话的则可以使用Optional静态方法of来把数据封装成Optional对象。

User user = new User();
Optional<User> userOptional = Optional.of(user);

但是要注意,如果使用of的时候传入的参数为null,会直接报出空指针异常。

2.3 empty

如果一个方法的返回值类型是Optional类型。而如果我们经判断发现某次计算得到的返回值为null,这个时候

就需要把null封装成Optional对象返回。这时则可以使用Optional静态方法empty来进行封装

Optional<Object> empty = Optional.empty();

它返回的就是一个Optional.empty对象。

2.4 ofNullable和of者的区别

  • of(T value)当参数value值为null时,of(T value)会报NullPointerException异常;

  • ofNullable(T value)当参数value值为null时,不会throw Exception,而是直接返回一个EMPTY对象,相当于调用了Optional.empty()

那是不是意味着,在项目中只用ofNullable函数而不用of函数呢?

也不一定不是的,一个东西存在那么自然有存在的价值。

当我们在运行过程中,不想隐藏NullPointerException。而是要立即报告,这种情况下就用Of函数。

但是这样的场景真的很少。

三、安全消费OPtional对象的值

获取到一个Optional对象后肯定需要对其中的数据进行使用。

这时候我们可以使用其ifPresent方法对来消费其中的值。

这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码。这样使用起来就更加安全了。

ifPresent方法分为有参和无参两种形式,源码如下:

public final class Optional<T> {
    
    
    //省略....
    public boolean isPresent() {
    
    
        return value != null;
    }
    //省略...
    public void ifPresent(Consumer<? super T> consumer) {
    
    
        if (value != null)
            consumer.accept(value);
    }
}

3.1 无参-isPresent()

它的作用就是即判断Optional中的value值是否为空,不为空就返回true,反之返回false

使用示例:

Optional<User> userOptional = Optional.ofNullable(user);
if (userOptional.isPresent()) {
    
    
    // TODO: do something
}

但是这种写法依旧丑陋,和以前这么写区别不大:

if (user != null){
    
    
   // TODO: do something
}

更加优雅的写法,就要借助下面的有参函数了。

3.2 有参-ifPresent(Consumer consumer)

它的作用就是在Optional中的value值不为空的时候,做一些操作,因此它的参数是一个消费者函数式接口。

示例如下:

Optional<User> userOptional = Optional.ofNullable(user);
userOptional.ifPresent(user -> System.out.println(user.getName()));

这样的写法就优雅的避免了空指针异常,也是安全消费的推荐写法。

四、获取Optional对象的值

一般来说,拥有了一个Optional对象以后,肯定是需要取出它内部的值来进行使用的。

4.1 get

Optional<User> userOptional = Optional.ofNullable(user);
User userGet = userOptional.get();

但是并不推荐使用这种写法。因为当Optional内部的数据为空的时候会出现异常。

五、安全获取Optional对象的值

如果期望安全的获取值。并不推荐使用get方法,而是使用Optional提供的以下方法。

5.1 orElse

User user = null;
user = Optional.ofNullable(user).orElse(new User());

Optional中的value值(user对象)为null的时候,会调用new User()去创建一个User对象进行返回。

5.2 orElseGet

User user = null;
user = Optional.ofNullable(user).orElseGet(() -> new User()));

作用和orElse一致,只是orElseGet接收一个Supplier类型的函数式接口。

Optional中的value值(user对象)为null的时候,会调用new User()去创建一个User对象进行返回。

5.3 orElseThrow

User user = null;
Optional.ofNullable(user).orElseThrow(()->new Exception("用户不存在"));

Optional中的value值(user对象)为null的时候,直接抛一个异常出去。

六、过滤Optional的值

6.1 filter

// 1.过滤单个对象
User user = new User();
Optional<User> userOptional = Optional.ofNullable(user);
userOptional.filter(item -> item.getAge()>50)
    		.ifPresent(item -> System.out.println(item.getName()));

// 2.过滤一个集合
// 创建一个Optional对象并存储一个List<User>集合
Optional<List<User>> optionalUsers = Optional.of(getUserList());
System.out.println(users);
List<User> filteredUsers = optionalUsers
    					.orElse(new ArrayList<>())
                        .stream()
                        .filter(user -> user.getAge() > 50).collect(Collectors.toList());
// 打印过滤后的用户列表
filteredUsers.forEach(System.out::println);

生成用户集合的方法:

private static List<User> getUserList() {
    
    
    List<User> userList = new ArrayList<>();
    userList.add(new User("Alice", 45));
    userList.add(new User("Bob", 55));
    userList.add(new User("Charlie", 60));
    return userList;
}

filter方法接受一个 Predicate 类型的函数式接口来对 Optional 中包含的值进行过滤。

如果包含的值满足条件,那么还是返回这个Optional;否则返回 Optional.empty

七、数据转换

7.1 map

Optional提供了map可以让我们的对数据进行转换,并且转换得到的数据也是被Optional包装好的,因此保证

了我们的使用安全。

Optional<List<User>> optionalUsers = Optional.ofNullable(getUserList());
Optional<List<Book>> optionalBooks = optionalUsers.map(user -> user.getBooks());

7.2 flatmap

它的用法和map几乎一样,但是它不会把结果再次包装在一个新的 Optional对象当中。

所以想要达到和map一样的效果,需要手动在Lambda表达式中把返回值转换为Optional对象。

Optional<List<User>> optionalUsers = Optional.ofNullable(getUserList());
Optional<List<Book>> optionalBooks = optionalUsers
    							.flatMap(user -> Optional.ofNullable(user.getBooks());

八、使用示例

8.1 例一

在方法中,老写法

public String getCity(User user)  throws Exception{
    
    
        if(user!=null){
    
    
            if(user.getAddress()!=null){
    
    
                Address address = user.getAddress();
                if(address.getCity()!=null){
    
    
                    return address.getCity();
                }
            }
        }
        throw new Excpetion("取值错误");
    }

Java8写法

public String getCity(User user) throws Exception{
    
    
    return Optional.ofNullable(user)
                   .map(u-> u.getAddress())
                   .map(a->a.getCity())
                   .orElseThrow(()->new Exception("取指错误"));
}

8.2 例二

以前写法

if(user!=null){
    
    
    dosomething(user);
}

Java8写法

 Optional.ofNullable(user).ifPresent(u->{
    
    
        // dosomething  xxx
});

8.3 例三

以前写法

public User getUser(User user) throws Exception{
    
    
    if(user!=null){
    
    
        String name = user.getName();
        if("ls".equals(name)){
    
    
            return user;
        }
    }else{
    
    
        user = new User();
        user.setName("ls");
        return user;
    }
}

Java8写法

public User getUser(User user) {
    
    
    return Optional.ofNullable(user)
                   .filter(u->"zs".equals(u.getName()))
                   .orElseGet(()-> {
    
    
                        User user1 = new User();
                        user1.setName("ls");
                        return user1;
                   });
}

采用这种链式编程,虽然代码优雅了。但是,逻辑性没那么明显,可读性有所降低,项目中可以看情况酌情使用。

猜你喜欢

转载自blog.csdn.net/qq_44749491/article/details/131527944