Optional最佳实践(对象操作利器)

news2025/1/21 11:59:08

前篇文章已经总结了集合的操作是如何在Java8优化的:函数编程和Stream_txxs的博客-CSDN博客,这篇文章总结一下对于对象如何利用Java8进行操作,这样对于大部分代码都可以用Java8的语法进行操作了。

一、 Optional 是什么

Optional 的作者 Brian Goetz 对这个 API 的说明:

Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.

为了避免 null 带来的错误,我们提供了一个可以明确表示空值的有限的机制。

Optional是一个容器,用于放置可能为空的值,它可以合理而优雅的处理null。

二、Optional API介绍

public static<T> Optional<T> empty();  // 返回一个Optional容器对象
 
public static <T> Optional<T> of(T value); // 创建一个Optional对象,如果 value 是 null,则抛出 NPE
 
public static <T> Optional<T> ofNullable(T value); // 创建一个Optional对象,但 value 为空时返回Optional.empty()
 
public T get(); // 返回Optional中包装的值,在判空之前,千万不要直接使用
 
public boolean isPresent(); // 判断Optional中是否有值,返回 boolean
 
public void ifPresent(Consumer<? super T> consumer); // 判断Optional中是否有值,有值则执行 consumer,否则什么都不干
 
public T orElse(T other); // 返回Optional中包装的值,但不同的是当取不到值时,返回你指定的 default
 
public T orElseGet(Supplier<? extends T> other); // 返回Optional中包装的值,取不到值时,返回你指定的 default
 
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier); // 返回Optional中包装的值,取不到值时抛出指定的异常

三、最佳实践

3.1、Optional(T value),empty(),of(T value),ofNullable(T value)

这四个函数之间具有相关性,因此放在一组进行记忆。

先说明一下,Optional(T value),即构造函数,它是private权限的,不能由外部调用的。其余三个函数是public权限,供我们所调用。那么,Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空。好吧,这么说还是比较抽象。直接上Optional(T value)构造函数的源码,如下图所示

那么,of(T value)的源码如下

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}

也就是说of(T value)函数内部调用了构造函数。根据构造函数的源码我们可以得出两个结论:

  • 通过of(T value)函数所构造出的Optional对象,当Value值为空时,依然会报NullPointerException。
  • 通过of(T value)函数所构造出的Optional对象,当Value值不为空时,能正常构造Optional对象。

除此之外呢,Optional类内部还维护一个value为null的对象,大概就是长下面这样的

public final class Optional<T> {
    //省略....
    private static final Optional<?> EMPTY = new Optional<>();
    private Optional() {
        this.value = null;
    }
    //省略...
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
}

那么,empty()的作用就是返回EMPTY对象。

好了铺垫了这么多,可以说ofNullable(T value)的作用了,上源码

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

好吧,大家应该都看得懂什么意思了。相比较of(T value)的区别就是,当value值为null时,of(T value)会报NullPointerException异常;ofNullable(T value)不会throw Exception,ofNullable(T value)直接返回一个EMPTY对象。

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

不是的,一个东西存在那么自然有存在的价值。当我们在运行过程中,不想隐藏NullPointerException。而是要立即报告,这种情况下就用Of函数。但是不得不承认,这样的场景真的很少。博主也仅在写junit测试用例中用到过此函数。

前置对象,以下所有的例子都适用这个对象进行操作

 @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class User {
        // 兴趣id列表
        private List<Long> favList;
        // 标签列表
        private List<Tag> tagList;

        private String name ="测试长度";
        private Optional<String> nameOp = Optional.ofNullable("测试op");

        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public static class Tag {
            private Long id;
        }

        @Override
        public String toString() {
            return "User{" +
                    "favList=" + favList +
                    ", tagList=" + tagList +
                    ", name='" + name + '\'' +
                    '}';
        }

        /**
         * 获取兴趣id列表
         */
        public List<Long> getFavList(User user) {
            return Optional.ofNullable(user)
                    .map(User::getFavList)
                    .map(Collection::stream)
                    .orElse(Stream.empty())
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
        }

        /**
         * 获取标签id列表
         */
        public List<Long> getTagIdList(User user) {
            return Optional.ofNullable(user)
                    .map(User::getTagList)
                    .map(Collection::stream)
                    .orElse(Stream.empty())
                    .map(Tag::getId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());

        }
    }

构造使用例子

    /**
     * 构造相关内容empty、of和ofNullable
     *
     */
    public static void create(){
        User user = null;
        System.out.println(Optional.empty());//输出对象
        System.out.println(Optional.of(user));//会直接抛出异常,因为of不能允许为空

        Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);
        System.out.println(user1);//Optional.empty,不会抛出异常

        User user2 = new User();
        Optional<User> user3 = Optional.ofNullable(user2).filter(u -> u.getName().length()<6);
        System.out.println(user3);//Optional[User{favList=null, tagList=null, name='测试长度'}],name需要赋值,不然u.getName()会抛出异常
    }

 3.2、orElse(T other),orElseGet(Supplier<? extends T> other)和orElseThrow(Supplier<? extends X> exceptionSupplier)


这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。orElse和orElseGet的用法如下所示,相当于value值为null时,给予一个默认值:

@Test
public void test() {
    User user = null;
    user = Optional.ofNullable(user).orElse(createUser());
    user = Optional.ofNullable(user).orElseGet(() -> createUser());
}
public User createUser(){
    User user = new User();
    user.setName("zhangsan");
    return user;
}

这两个函数的区别:当user值不为null时,orElse函数依然会执行createUser()方法,而orElseGet函数并不会执行createUser()方法,大家可自行测试。
至于orElseThrow,就是value值为null时,直接抛一个异常出去,用法如下所示

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

例子

    /**
     * 构造函数传入的value值为null时,进行调用的
     * orElse(T other),orElseGet(Supplier<? extends T> other)和orElseThrow(Supplier<? extends X> exceptionSupplier)
     * 这两个函数的区别:当user值不为null时,orElse函数依然会执行createUser()方法,而orElseGet函数并不会执行createUser()方法
     */
    public static void ifnull(){
        User user = null;//User user = new User();orElseGet就不执行createUser方法了
        User user1 = Optional.ofNullable(user).orElse(createUser());
        System.out.println(user1);//User{favList=null, tagList=null, name='测试长度'}
        User user2 = Optional.ofNullable(user).orElseGet(() -> createUser());
        System.out.println(user2);//User{favList=null, tagList=null, name='测试长度'}
        try {
            //会抛出异常,所以需要捕获异常
            Optional.ofNullable(user).orElseThrow(()->new Exception("用户不存在"));
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
    public static User createUser(){
        System.out.println("方法被执行");
        return new User();
    }

3.3、map(Function<? super T, ? extends U> mapper)和flatMap(Function<? super T, Optional> mapper)

这两个函数放在一组记忆,这两个函数做的是转换值的操作。直接上源码

public final class Optional<T> {
    //省略....
     public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }
    //省略...
     public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }
}

这两个函数,在函数体上没什么区别。唯一区别的就是入参,map函数所接受的入参类型为Function<? super T, ? extends U>,而flapMap的入参类型为Function<? super T, Optional<U>>

在具体用法上,对于map而言:

如果User结构是下面这样的

public class User {
    private String name;
    public String getName() {
        return name;
    }
}

这时候取name的写法如下所示

String city = Optional.ofNullable(user).map(u-> u.getName()).get();

对于flatMap而言:

如果User结构是下面这样的

public class User {
    private String name;
    public Optional<String> getName() {
        return Optional.ofNullable(name);
    }
}

这时候取name的写法如下所示

String city = Optional.ofNullable(user).flatMap(u-> u.getName()).get();

例子

    /**
     * map和flatMap,两者的区别是前者是正常的对象,后者是Optional对象
     */
    public static void opmap(){
        User user = new User();
        String name = Optional.ofNullable(user).map(u-> u.getName()).get();
        System.out.println(name);//测试长度

        String nameOp = Optional.ofNullable(user).flatMap(u-> u.getNameOp()).get();
        System.out.println(nameOp);//测试op

        //及联map
        Optional.ofNullable(user)
                .map(o -> o.getTagList())
                .map(d -> d.get(0))
                .filter(d -> d.getId() > 1)
                .orElse(new User.Tag());

    }

3.4、isPresent()和ifPresent(Consumer<? super T> consumer)

这两个函数放在一起记忆,isPresent即判断value值是否为空,而ifPresent就是在value值不为空时,做一些操作。这两个函数的源码如下

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

需要额外说明的是,大家千万不要把

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

给写成

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

因为这样写,代码结构依然丑陋。博主会在后面给出正确写法

至于ifPresent(Consumer<? super T> consumer),用法也很简单,如下所示

Optional.ofNullable(user).ifPresent(u->{
    // TODO: do something
});

例子:

    /**
     * isPresent()和ifPresent(Consumer<? super T> consumer)
     */
    public static void ifpre(){
        User user = new User();
        if (user != null){
            // do something
        }
        //上边这种形式千万不要写成下边这种形式,这样写,代码结构依然丑陋
        Optional<User> user1 = Optional.ofNullable(user);
        if (user1.isPresent()){
            // do something
        }

        //正确写法场景1
        user1.orElseGet(() -> new User());
        //而不是
        if(user1.isPresent()){
            user1 = user1;
        }else {
            user1 = Optional.ofNullable(new User());
        }

        //正确写法2
        user1.ifPresent(System.out::println);
        //而不是
        if (user1.isPresent()) {
            System.out.println(user1.get());
        }

        //正确写法3
        user1.map(u -> u.getFavList()).orElse(Collections.emptyList());
        //而不是
        if(user1.isPresent()) {
            List<Long> result =  user1.get().getFavList();
        } else {
            List<Long> result =  Collections.emptyList();
        }

        //正确写法4,由于map可以无限及联
        user1.map(u -> u.getName())
                .map(name -> name.toUpperCase())
                .orElse(null);
        //而不是
        User user3 = new User();
        String name = null;
        if(user3 != null) {
            name = user.getName();
            if(name != null) {
                 name.toUpperCase();
            } else {
                user = null;
            }
        } else {
            user = null;
        }

        //正确写法5
        Optional.ofNullable(user)
                .ifPresent(u->{
                    //dosomething(u);
                });
        //而不是
        if(user!=null){
            //dosomething(user);
        }

        //正确写法6
        Optional.ofNullable(user).map(u -> u.getName()).orElse("unknown");
        //而不是
        Optional<User> u = Optional.ofNullable(user);
        if(!u.isPresent()){
            //dosomething
        }else{
            //dosomething
        }
    }

3.5、filter(Predicate<? super T> predicate)

不多说,直接上源码

public final class Optional<T> {
    //省略....
   Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
}

filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty

用法如下

Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);

如上所示,如果user的name的长度是小于6的,则返回。如果是大于6的,则返回一个EMPTY对象。

例子:

    public static void useFilter(){
        User user2 = new User();
        Optional<User> user3 = Optional.ofNullable(user2).filter(u -> u.getName().length()<6);
        System.out.println(user3);//Optional[User{favList=null, tagList=null, name='测试长度'}],name需要赋值,不然u.getName()会抛出异常
    }

四、简单总结:

1. 不要用get()和orElse(),而用orElseGet();
2. 不要直接返回null,使用 Optional.empty()
3. 不要用of(),而用ofNullable()
4. 不要使用isPresent(),get(),这是倒退(轻一点叫不推荐)
5. 大胆使用map、filter 重构代码
6. Optional不建议用于函数的入参
7. Optional不能被序列化,因此不能使用于字段


五、参考


https://blog.csdn.net/jack1liu/article/details/124064925
https://caoyunhao.com/p/c794fc/
https://xie.infoq.cn/article/e3d1f0f4f095397c44812a5be
https://www.aqwdzy.com/content/84
https://zhuanlan.zhihu.com/p/128481434
https://www.jianshu.com/p/362010f310b9
https://juejin.cn/post/7174292017415979038
https://juejin.cn/post/6962035387787116551
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/161109.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

收购淘米后,MMV加速走向迪士尼式IP开发之旅

一家纯正的元宇宙控股公司与经典IP公司碰撞&#xff0c;能擦出怎样的火花&#xff1f;这个问题或将在不远的将来得出答案。 1月12日晚间&#xff0c;刚在一周前以SPAC方式登陆纳斯达克的MMV&#xff08;MultiMetaVerse&#xff0c;即“元宇宙控股”&#xff09;&#xff0c;宣…

C语言进阶——动态内存管理

目录 一. 为什么存在动态内存分配 二. 动态内存函数 1.malloc 2.free 3.calloc 4.realloc 三. 常见的动态内存错误 一. 为什么存在动态内存分配 在此之前&#xff0c;我们为数组开配空间都是这样的 int nums[10]{0}; 但这样会有很大的局限性 1. 空间开辟大…

十三、Gtk4-TfeTextView函数

TfeTextView相关函数在这一章节介绍 1 tfetextview.h 头文件tfetextview.h提供了: TfeTextView的类型&#xff0c;是TFE_TYPE_TEXT_VIEW。G_DECLARE_FINAL_TYPE的扩展包含了一些有用的宏。定义了open-response信号的常量。tfetextview.c的公共函数被声明。 因此&#xff0c…

「布道人生」第一期:阿里云DevOps资深专家章屹

本期嘉宾——章 屹 阿里云 DevOps 资深专家。2012 年加入阿里巴巴&#xff0c;十年如一日专注在 DevOps 领域的理论、咨询、解决方案和产品技术工作上。作为 CIO 学院和阿里云大学的讲师&#xff0c;为众多大型企业管理高层讲授 DevOps 课程&#xff0c;探讨通过 DevOps 提升企…

drawCell | 不会画细胞结构图就用这个R包吧~ Super Nice!~

1写在前面 我们在paper中经常需要画到细胞结构图&#xff0c;新手ppt一点一点画&#xff0c;高手可能会用AI手搓&#xff0c;土豪直接使用BioRender。&#x1f912; 今天给大家大家分享一个代码画细胞结构图的R包&#xff0c;如果你觉得自己不会写代码&#xff0c;不想看了&…

python基础篇之元组、字典(增删改查)

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a;lqj_本人的博客_CSDN博客-微信小程序,前端,vue领域博主lqj_本人擅长微信小程序,前端,vue,等方面的知识https://blog.csdn.net/lbcyllqj?spm1000.2115.3001.5343 哔哩哔哩欢迎关注&…

震旦ad188复印机报机器故障维修召唤c0211

故障现象: 手送走纸的机器出现嗒嗒的异响,走几张还会跳0211,经常出现卡纸等现象,或者报C0211; 故障分析: 维修召唤C0211可能是感光鼓和载体寿命到期࿰

云原生|Java二级高速缓存架构设计

为什么使用缓存 缓存&#xff0c;主要有两个用途&#xff1a;提高服务性能和并发。 缓存是提高服务响应速度最快的方式之一。 我们设计缓存的目的是减少用户直接访问磁盘、访问网络带来的性能损耗&#xff0c;把磁盘、网络请求的内容存在在内存中&#xff0c;提升应用程序的…

【Doris】Doris数据库最新版安装方法,详细图文教程

环境安装 Doris 作为一款开源的 MPP 架构 OLAP 数据库&#xff0c;能够运行在绝大多数主流的商用服务器上。为了能够充分运用 MPP 架构的并发优势&#xff0c;以及 Doris 的高可用特性&#xff0c;我们建议 Doris 的部署遵循以下需求&#xff1a; Linux 操作系统版本需求Linu…

研讨会回顾 | UI自动化测试现场演示

2022年12月6日&#xff0c;龙智与软件测试自动化“领导者”SmartBear联合举办了主题为“如何通过自动化测试实现降本、增效与提质”的在线研讨会。此次研讨会中&#xff0c;龙智技术工程师邱洁玉现场演示了使用UI自动化测试的过程&#xff0c;并简要介绍了API的自动化测试。 软…

时钟频率与时间单位的换算

1. 频率、时间 各自的单位的关系 频率&#xff1a; 1GHz 1000MHz、1MHz 1000KHz、1KHz 1000Hz 1GHz 103MHz 106KHz 109Hz 时间&#xff1a; 1s 1000ms、1ms 1000μs、1μs 1000ns [注]&#xff1a;s (秒)、ms (毫秒)、μs (微秒)、ns (纳秒)。 2. 时间 和 频率 的换…

jsp税务管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 jsp 税务管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开 发&#xff0c;数据库为Mysql&#xff0c;使用ja…

【SpringCloud10】OpenFeign服务接口调用

1.概述 1.1OpenFeign是什么 官网 Feign是一个声明式WebService客户端&#xff0c;使用Feign能让编写Web Service客户端更加简单。 它的使用方法是定义一个服务接口然后在上面添加注解&#xff0c;Feign也支持可拔插式的编码器和解码器&#xff0c;Spring Cloud对Feign进行了…

设计模式-JDBC中的桥接模式

一、首先看整个brige模式的结构图。如下&#xff1a;Abstraction — 抽象化角色&#xff1a;定义抽象的接口&#xff0c;包含一个对实现化角色的引用Refined Abstraciotn — 扩展抽象化角色&#xff1a;抽象化角色的子类&#xff0c;实现父类中的业务方法&#xff0c;并通过组合…

录屏专家怎么用?录屏软件使用教程(附下载)

想要更好地录制电脑屏幕可以使用电脑录屏专家&#xff0c;但有很多人在安装录屏专家之后&#xff0c;不知道如何使用。录屏专家怎么用&#xff1f;怎样使用录屏专家录制电脑屏幕&#xff1f;下面小编给您分享录屏软件使用的教程&#xff08;附安装教程&#xff09;&#xff0c;…

RabbitMQ实战:性能和安全

本系列是「RabbitMQ实战&#xff1a;高效部署分布式消息队列」书籍的总结笔记。 前两篇介绍了RabbitMQ在可用性、监控方面的考虑&#xff0c;这是基础保障&#xff0c;因为在某些场景下是不容许丢失消息的&#xff0c;但它和性能往往是对立的&#xff0c;需要根据业务场景做取舍…

JSP SSM众包网站系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 JSP SSM众包网站系统 是一套完善的系统源码&#xff0c;对理解JSP java SrpingMVC mybiats 框架 MVC编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;以及相应配套的设计文档&#xff0c;系统主要采用B/S模式开发。 研究的基本内容…

JUC并发编程学习笔记——CAS个人理解

1. CAS引出 1.1 悲观锁 顾名思义&#xff0c;就是比较悲观的锁&#xff0c;总是假设最坏的情况&#xff0c;每次去拿数据的时候都认为别人会修改&#xff0c;所以每次在拿数据的时候都会上锁&#xff0c;这样别人想拿这个数据就会阻塞直到它拿到锁&#xff08;共享资源每次只…

什么是 HTTP?

什么是 HTTP&#xff1f; 超文本传输协议 (HTTP) 是万维网的基础&#xff0c;用于通过超文本链接加载网页。HTTP 是应用程序层协议&#xff0c;旨在在联网设备之间传输信息&#xff0c;并在网络协议栈的其他层之上运行。HTTP 上的典型流涉及客户端计算机向服务器发出请求&…

2023CUPT第十四题 射流的折射 思路与解法

文章未完首先声明一点&#xff0c;这个题我还没想好最后的答案&#xff0c;欢迎交流&#xff08;备注&#xff1a;博主今年不参加CUPT 博主去做电子设计大赛了&#xff01;有电赛谁还玩CUPT啊&#xff09;声明2&#xff1a;博主没有学过流体力学所以。。声明3&#xff1a;博主的…