Optional判空

JAVA在1.8版本推出Optional,官方文档将其描述为可能包含或不包含非空值的容器对象,目前Optional用于避免程序出现异常NullPointerException。

代码模拟

// 下面所有类省略set,get方法
public class Employee {
    private String employeeName;
    private Team team;
}

public class Team {
    private String teamName;
    private Department department;

    public Team(String teamName) {
        this.teamName = teamName;
    }
}

public class Department {
    private String departmentName;
    private Company company;
}

public class Company {
    private String companyName;
}

测试代码

// 在创建时因为没有初始化Team对象的Department属性,导致后续调用空指针
public void testCompany(){
    Employee employee = new Employee();
    employee.setEmployeeName("zhangsan");
    employee.setTeam(new Team("xxx产品组"));
    System.out.println(employee.getTeam().getDepartment().getCompany().getCompanyName());
}

这时如果我们采用传统方式一般判空代码如下

public void testCompanyAvoidNPE(){
    Employee employee = new Employee();
    employee.setEmployeeName("zhangsan");
    employee.setTeam(new Team("xxx产品组"));
    Team team = employee.getTeam();
    if (team == null){
        System.out.println("异常拉,参数为空!");
        return;
    }
    Department department = team.getDepartment();
    if (department == null){
        System.out.println("异常拉,参数为空!");
        return;
    }
    Company company = department.getCompany();
    if (company == null){
        System.out.println("异常拉,参数为空!");
        return;
    }
    String companyName = company.getCompanyName();
    System.out.println(companyName);
}

显然这种判空代码造成了业务代码膨胀,代码可读性极低,所以在这种场景下我们需要学习1.8推出的判空容器对象Optional。

Optional常用方法

创建 Optional 对象

JAVA提供了三个静态方法用于构建Optional对象如下所示

返回值 方法和描述
static <T> Optional<T> empty()  返回一个空的 Optional实例。
static <T> Optional<T> of(T value)  返回具有 Optional的当前非空值的Optional。
static <T> Optional<T> ofNullable(T value)  返回一个 Optional指定值的Optional,如果非空,则返回一个空的  Optional 。
public void createOptionalObject(){
    System.out.println(Optional.empty());
    // 传null报空指针
    // System.out.println(Optional.of(null));
    System.out.println(Optional.of(new String("1111")));
    // 传null调用Optional.empty()
    System.out.println(Optional.ofNullable(null));
    System.out.println(Optional.ofNullable(new Content("111","测试内容")));
}

class Content {
    private String id;
    private String value;

    public Content() {
    }

    public Content(String id, String value) {
        this.id = id;
        this.value = value;
    }
    // 省略get,set方法
}

执行结果如下

Optional.empty
Optional[1111]
Optional.empty
Optional[Content{id='111', value='测试内容'}]

在真正使用时建议使用ofNullable,因为它能处理空值或者非空值,代码如下

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

常用功能方法

方法名 返回值 功能描述
isPresent() boolean 判断Optional对象是否存在,存在true不存在false
ifPresent(Consumer<? super T> consumer) void 如果Optional对象存在执行consumer消费型接口,不存在不执行
get T 返回Optional对象封装的对象T数据,注意实际对象可能为空会抛出异常
orElse(T other) T 如果Optional对象存在则返回封装的对象数据,如果不存在返回T other数据,相当于不存在给默认值
orElseGet(Supplier<? extends T> other) T 是orElse方法的升级版,区别在于orElse方法传入的是一个固定默认值,而此方法是一个供给型函数方法,如果Optional对象为空则执行other的方法逻辑
orElseThrow(Supplier<? extends X> exceptionSupplier) <X extends Throwable>T 一样是orElse方法的升级版,当Optional对象为空执行exceptionSupplier方法,最后抛出异常
filter(Predicate<? super T> predicate) Optional<T> 当Optional对象满足predicate断言函数中的匹配规则则返回,否则返回空Optional
map(Function<? super T,? extends U> mapper) <U> Optional<U> 能够将Optional的对象值处理转换为另一个实例对象值,并生成新类型的Optional对象,如果生成的新对象为null,则返回一个空Optional对象
flatMap(Function<? super T,Optional<U>> mapper) <U> Optional<U> 和map类似,不过map操作的是具体对象,而flatMap返回的是Optional封装过的对象

代码演示如下

    public void testOptionFunction(){
        //===============================isPresent()================================
        // false
        System.out.println(Optional.empty().isPresent());
        // Optional[Content{id='222', value='测试'}]
        System.out.println(Optional.ofNullable(new Content("222","测试")));

        //===============================ifPresent()================================
        // 不为空打印:Content{id='222', value='测试'}  为空不执行
        Optional.ofNullable(new Content("222","测试")).ifPresent(e->{
            System.out.println("不为空打印:" e);
        });

        //===============================get()================================
        // NoSuchElementException: No value present
        try {
            System.out.println(Optional.empty().get());
        }catch (Exception e){
            e.printStackTrace();
        }
        // Content{id='222', value='测试'}
        System.out.println(Optional.ofNullable(new Content("222","测试")).get());

        //===============================orElse()================================
        // Content{id='0', value='默认'}
        System.out.println(Optional.empty().orElse(new Content("0","默认")));

        //===============================orElseGet()================================
        // Content{id='333', value='测试orElseGet'}
        System.out.println(Optional.empty().orElseGet(() -> {
            Content content = new Content("333", "测试orElseGet");
            return content;
        }));

        // Content{id='222', value='测试'}
        System.out.println(Optional.ofNullable(new Content("222", "测试")).orElseGet(() -> {
            Content content = new Content("444", "测试orElseGet");
            return content;
        }));

        //===============================orElseThrow()================================
        // java.lang.Exception: 参数为空
        try {
            Optional.empty().orElseThrow(()->{
               return new Exception("参数为空");
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

        //===============================filter()================================
        // "555".equals(e.getId());   Optional[Content{id='555', value='测试'}]
        // "5565".equals(e.getId());  Optional.empty
        System.out.println(Optional.ofNullable(new Content("555", "测试")).filter(e -> {
            return "5565".equals(e.getId());
        }));
    }

map和flatMap

public void testOptionMapFunction(){
    Optional<Content> optionalContent = Optional.ofNullable(new Content("777","测试Map"));

    Optional<Content> optionalContent1 = optionalContent.map(e -> {
        String id = e.getId();
        String value = e.getValue();
        return e;
    });

    Optional<Content> optionalContent2 = optionalContent.flatMap(e -> {
        String id = e.getId();
        String value = e.getValue();
        // 不同点在这里,一个是可以转换为另一个实例对象,一个是转换为Optional包装对象
        return Optional.of(e);
    });
    // 最终效果实现效果类似
    System.out.println(optionalContent1);
    System.out.println(optionalContent2);
}

使用Optional一定比null好吗

这个显然是不对的,如果使用方式如下所示,其实和null的直接判空没有区别

public static void main(String[] args) {
    Optional<Content> optionalContent = Optional.ofNullable(null);
    // 直接报错
    optionalContent.get();
}

// 升级写法
public static void main(String[] args) {
    Optional<Content> optionalContent = Optional.ofNullable(null);
    // 非空判断
    if (optionalContent.isPresent()){
        System.out.println(optionalContent.get());
    }
}

null的直接判空

public static void main(String[] args) {
    Content content = new Content();
    if (content != null){
        System.out.println(content);
    }
}

正确写法如下所示

public static void main(String[] args) {
    Optional<Content> optionalContent = Optional.ofNullable(null);
    // 非空时执行消费型接口里面的逻辑
    optionalContent.ifPresent(content -> {
        System.out.println(content);
    });
}

到这里有人可能说,如果if{}else{}里面都需要写逻辑如何处理呢

public static void main(String[] args) {
    Optional<Content> optionalContent = Optional.ofNullable(null);
    Content content = optionalContent.map(e -> {
        // 做一些业务
        // 返回值可以为任意类型值,可以返回字符串,不过orElseGet需要返回字符串保持一致即可
        return e;
    }).orElseGet(() -> {
        // 做一些业务
        return new Content("0", "默认值");
    });
    System.out.println(content);
}

Optional 使用场景

减少繁琐的非空判断

如前面提到的testCompanyAvoidNPE方法中的例子,就可以采用Optional来简化

public void testCompanyAvoidNPE2(){
    Employee employee = new Employee();
    employee.setEmployeeName("zhangsan");
    employee.setTeam(new Team("xxx产品组"));
    // 这里相当于是链式操作
    String companyName = Optional.ofNullable(employee)
        .map(employee1 -> employee1.getTeam())
        .map(team -> team.getDepartment())
        .map(department -> department.getCompany())
        .map(company -> company.getCompanyName())
        .orElse("no company");
    System.out.println(companyName);
}

设置默认值兜底

// 原始方法
public String setDefaultValue1(Content content){
    if (ObjectUtils.isEmpty(content)){
        return "0";
    }
    if (StringUtils.isEmpty(content.getId())){
        String id = "0";
        content.setId(id);
    }
    return content.getId();
}

// 采用Optional
public String setDefaultValue2(Content content){
    String id = Optional.ofNullable(content)
        .map(content1 -> content1.getId())
        .orElse("0");
    return id;
}

Optional 尽量只用作方法的返回类型

注意我们采用Optional的最终目的是避免程序中出现null对象异常的情况,所以我们封装方法的时候可以采用Optional 作为方法的返回值类型,但也要注意Optional虽好但不要滥用,适当使用即可。

以上就是Java Optional的判空操作详解的详细内容,更多关于Java Optional判空操作的资料请关注Devmax其它相关文章!

Java Optional的判空操作详解的更多相关文章

  1. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  3. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  4. Java实现世界上最快的排序算法Timsort的示例代码

    Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序和二分插入排序算法的混合体,号称世界上最好的排序算法。本文将详解Timsort算法是定义与实现,需要的可以参考一下

  5. Java日期工具类的封装详解

    在日常的开发中,我们难免会对日期格式化,对日期进行计算,对日期进行校验,为了避免重复写这些琐碎的逻辑,我这里封装了一个日期工具类,方便以后使用,直接复制代码到项目中即可使用,需要的可以参考一下

  6. Java设计模式之模板方法模式Template Method Pattern详解

    在我们实际开发中,如果一个方法极其复杂时,如果我们将所有的逻辑写在一个方法中,那维护起来就很困难,要替换某些步骤时都要重新写,这样代码的扩展性就很差,当遇到这种情况就要考虑今天的主角——模板方法模式

  7. Java 中 Class Path 和 Package的使用详解

    这篇文章主要介绍了Java 中 Class Path和Package的使用详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下

  8. java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源)

    这篇文章主要介绍了java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源),文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

  9. Java一维数组和二维数组元素默认初始化值的判断方式

    这篇文章主要介绍了Java一维数组和二维数组元素默认初始化值的判断方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  10. java实现emqx设备上下线监听详解

    这篇文章主要为大家介绍了java实现emqx设备上下线监听详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部