Java8 Optional

简介: Java8 Optional

Optional和Stream虽然都是Java8的新特性,但据我观察Optional的使用频率远低于Stream,究其原因是大家对它有误解。很多人以为Optional是用来“消除”空指针的,所以当他们发现即便使用了Optional还会抛异常时,感到非常地失望,甚至是愤怒。比如当value确实为null时,直接调用Optional#get()会抛出NoSuchElementException:

// Optional#get()底层源码,当value为null时抛出NoSuchElementException,虽然不是NPE,但也是异常
public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

这实在错怪Optional了!NPE是Java语言机制的一环,单靠一个Optional类如何能够消除呢?Optional的目的不是“消除空指针”,而是优雅地做空指针“探测”。就好比给了你一个排雷工具,但你就是不按正确方法使用它,最终被炸死了,这能怪谁呢?地雷是客观存在的,不可消除。你能做的就是好好利用排雷工具,避免地雷引爆。

再说回上面的Optional#get(),很多人觉得:妈的,好不容易Optional包装了null,结果又提供了一个可能抛异常的get方法,意义何在?实际上NPE之所以让人讨厌,不仅仅因为它是一个异常(我们日常开发遇到的异常还少吗),而是因为NPE往往会掩盖确切的错误信息。举个例子:

public void method1() {
    User user = userService.getById(1L);
    this.method2(user, 999);
}
public void method2(User user, Integer point) {
    // 省略10+代码
    updatePoint(user.getId(), point);
}

抛异常的是第8行的updatePoint()方法,而实际上“错误源头”是第2行的user,这会给我们排查问题造成干扰,特别是实际项目中往往调用链路更加复杂。如果使用Optional#get(),那么在get获取user的时候就会直接报错,排查问题会简单很多!

我个人基本不用Optional#get(),更习惯用orElse或orElseThrow()处理

推荐使用场景

第一个场景就是简化空指针探测,比如:

public static String getDepartmentNameOfUser(String username) {
    ResultTO<User> resultTO = getUserByName(username);
    if (resultTO != null) {
        User user = resultTO.getData();
        if (user != null) {
            Department department = user.getDepartment();
            if (department != null) {
                return department.getName();
            }
        }
    }
    return "未知部门";
}

解决办法就是3个步骤:

  • 包装value:Optional.ofNullable()
  • 逐层安全地拆解value:map()
  • 最终返回:orElse()/orElseGet()/orElseThrow
public static String getDepartmentNameOfUser(String username) {
    return Optional.ofNullable(getUserByName(username))
            .map(ResultTO::getData)
            .map(User::getDepartment)
            .map(Department::getName)
            .orElse("未知部门");
}

其他的还可以是:

public boolean sendMessage(Long fromId, Long toId, String message) {
    // 用户校验:如果用户不存在,直接抛异常
    User user = Optional.ofNullable(userService.getUserById(fromId))
            .orElseThrow(() -> new BizException(ErrorEnumCode.USER_NOT_EXIST));
    
    // 组装数据并发送...
}
public List<String> listSubCities(String provinceCode) {
    // 查到就返回,查不到就返回替代值(对于集合而言,尽量返回空集合)
    return Optional.ofNullable(getCitiesByPid(provinceCode)).orElse(new ArrayList<String>());
}

另外,如果你需要对返回值进行判断,比如结果是否大于某个值等,可以使用Optional的filter方法:

public class OptionalFilterTest {
    public static void main(String[] args) {
        // 需求:调用getUser()得到person,并且person的age大于18才返回username,否则返回不存在
        
        // 普通的写法(如果层级深一点会很难看)
        Person user = getUser();
        if (user != null && user.getAge() > 18) {
            System.out.println(user.getName());
        } else {
            System.out.println("不存在");
        }
        // 你尝试用map(),但你发现直接返回username了,你甚至无法再次判断是否age>18
        String username1 = Optional.ofNullable(getUser())
                .map(Person::getName)
                .orElse("不存在");
        System.out.println("username1 = " + username1);
        // 引入filter()
        String username2 = Optional.ofNullable(getUser())
                .filter(person -> person.getAge() > 18)
                .map(Person::getName)
                .orElse("不存在");
        System.out.println("username2 = " + username2);
    }
    public static Person getUser() {
        if (RandomUtils.nextBoolean()) {
            return null;
        } else {
            Person person = new Person();
            person.setName("鲍勃");
            // commons.lang3
            person.setAge(RandomUtils.nextInt(0, 50));
            return person;
        }
    }
    @Data
    static class Person {
        private String name;
        private Integer age;
    }
}
目录
相关文章
|
7月前
|
Java Unix 程序员
java 8 新特性讲解Optional类--Fork/Join 框架--新时间日期API--以及接口的新特性和注解
java 8 新特性讲解Optional类--Fork/Join 框架--新时间日期API--以及接口的新特性和注解
98 1
|
4月前
|
安全 Java API
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
128 0
|
3月前
|
设计模式 Java
结合HashMap与Java 8的Function和Optional消除ifelse判断
`shigen`是一位致力于记录成长、分享认知和留住感动的博客作者。本文通过具体代码示例探讨了如何优化业务代码中的if-else结构。首先展示了一个典型的if-else处理方法,并指出其弊端;然后引入了策略模式和工厂方法等优化方案,最终利用Java 8的Function和Optional特性简化代码。此外,还提到了其他几种消除if-else的方法,如switch-case、枚举行、SpringBoot的IOC等。一起跟随shigen的脚步,让每一天都有所不同!
40 10
结合HashMap与Java 8的Function和Optional消除ifelse判断
|
7月前
|
自然语言处理 Java API
Java 8的Stream API和Optional类:概念与实战应用
【5月更文挑战第17天】Java 8引入了许多重要的新特性,其中Stream API和Optional类是最引人注目的两个。这些特性不仅简化了集合操作,还提供了更好的方式来处理可能为空的情况,从而提高了代码的健壮性和可读性。
179 7
|
4月前
|
安全 Java API
Java 8 流库的魔法革命:Filter、Map、FlatMap 和 Optional 如何颠覆编程世界!
【8月更文挑战第29天】Java 8 的 Stream API 通过 Filter、Map、FlatMap 和 Optional 等操作,提供了高效、简洁的数据集合处理方式。Filter 用于筛选符合条件的元素;Map 对元素进行转换;FlatMap 将多个流扁平化合并;Optional 安全处理空值。这些操作结合使用,能够显著提升代码的可读性和简洁性,使数据处理更为高效和便捷。
150 0
|
6月前
|
Java API 容器
Java 8 的流库:Filter、Map、FlatMap 及 Optional 的概念与用法
【6月更文挑战第9天】Java 8 引入了许多强大的新特性,其中流库(Stream API)和 Optional 类极大地简化了集合操作和空值处理。本文将深入探讨 filter、map、flatMap 以及 Optional 的概念和用法,并提供示例代码来展示其实际应用。
87 4
|
5月前
|
安全 Java 数据处理
Java面试题:什么是Java中的Optional类及其使用场景?
Java面试题:什么是Java中的Optional类及其使用场景?
71 0
|
5月前
|
Java 容器
Java中使用Optional类的建议
Java中使用Optional类的建议
|
6月前
|
安全 NoSQL Java
探索Java Optional类:构造器、成员变量与方法
探索Java Optional类:构造器、成员变量与方法
|
7月前
|
安全 Java 开发者
Java一分钟之-Optional类:优雅处理null值
【5月更文挑战第13天】Java 8的`Optional`类旨在减少`NullPointerException`,提供优雅的空值处理。本文介绍`Optional`的基本用法、创建、常见操作,以及如何避免错误,如直接调用`get()`、误用`if (optional != null)`检查和过度使用`Optional`。正确使用`Optional`能提高代码可读性和健壮性,建议结合实际场景灵活应用。
296 3